diff --git a/.gitattributes b/.gitattributes index ad0703b1d1dcabd1b91d9afaea1f83aef47c6a01..c2640408f57b98d540e1b84f6d4b63c55e6d2a53 100644 --- a/.gitattributes +++ b/.gitattributes @@ -22,6 +22,28 @@ testfont binary /src/mapping.c foreign_ident /src/modules/Gdbm/gdbmmod.c foreign_ident /src/modules/Gmp/mpz_glue.c foreign_ident +/src/modules/Gz/zlibmod.c foreign_ident +/src/modules/Image/blit.c foreign_ident +/src/modules/Image/dct.c foreign_ident +/src/modules/Image/font.c foreign_ident +/src/modules/Image/image.c foreign_ident +/src/modules/Image/image.h foreign_ident +/src/modules/Image/lzw.c foreign_ident +/src/modules/Image/lzw.h foreign_ident +/src/modules/Image/matrix.c foreign_ident +/src/modules/Image/operator.c foreign_ident +/src/modules/Image/pattern.c foreign_ident +/src/modules/Image/pnm.c foreign_ident +/src/modules/Image/quant.c foreign_ident +/src/modules/Image/togif.c foreign_ident +/src/modules/Mysql/Makefile.in foreign_ident +/src/modules/Mysql/acconfig.h foreign_ident +/src/modules/Mysql/configure.in foreign_ident +/src/modules/Mysql/mysql.c foreign_ident +/src/modules/Mysql/precompiled_mysql.h foreign_ident +/src/modules/Mysql/result.c foreign_ident +/src/modules/Pipe/pipe.c foreign_ident +/src/modules/Ssleay/ssleay.c foreign_ident /src/modules/_Crypto/cbc.c foreign_ident /src/modules/_Crypto/crypto.c foreign_ident /src/modules/_Crypto/crypto.h foreign_ident diff --git a/src/modules/Gz/.cvsignore b/src/modules/Gz/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..b60f1913a1ded943910c93507177d28ab055c55c --- /dev/null +++ b/src/modules/Gz/.cvsignore @@ -0,0 +1,9 @@ +.pure +Makefile +config.log +config.status +configure +dependencies +linker_options +stamp-h +zlib_machine.h diff --git a/src/modules/Gz/.gitignore b/src/modules/Gz/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f4cfce0a1e458466cce958d18029e91a3f73c208 --- /dev/null +++ b/src/modules/Gz/.gitignore @@ -0,0 +1,9 @@ +/.pure +/Makefile +/config.log +/config.status +/configure +/dependencies +/linker_options +/stamp-h +/zlib_machine.h diff --git a/src/modules/Gz/Makefile.in b/src/modules/Gz/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..5b2722ba6fdbe3f505d116d289f2140a1e6ecd5b --- /dev/null +++ b/src/modules/Gz/Makefile.in @@ -0,0 +1,7 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=zlibmod.o +MODULE_LDFLAGS=@LIBS@ + +@dynamic_module_makefile@ +@dependencies@ diff --git a/src/modules/Gz/configure.in b/src/modules/Gz/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..4576722861dd7696625c27fd835ae06a51a20b63 --- /dev/null +++ b/src/modules/Gz/configure.in @@ -0,0 +1,17 @@ +AC_INIT(zlibmod.c) +AC_CONFIG_HEADER(zlib_machine.h) +AC_ARG_WITH(zlib, [ --with(out)-zlib Support gzip compression],[],[with_zlib=yes]) + +sinclude(../module_configure.in) + +if test x$with_zlib = xyes ; then + AC_CHECK_HEADERS(zlib.h) + if test $ac_cv_header_zlib_h = yes ; then + AC_CHECK_LIB(z, compress, [[LIBS="${LIBS-} -lz"]], + AC_CHECK_LIB(gz, compress)) + fi +fi + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) + + diff --git a/src/modules/Gz/doc/gz_deflate b/src/modules/Gz/doc/gz_deflate new file mode 100644 index 0000000000000000000000000000000000000000..7873bfcf42f182718c72942e53c2ca53e6f0fdf4 --- /dev/null +++ b/src/modules/Gz/doc/gz_deflate @@ -0,0 +1,61 @@ +NAME + Gz_deflate - gzip packer + +DESCRIPTION + Gz_inflate is a builtin program written in C. It interfaces the + packing routines in the libz library. + +NOTA BENE + This program is only available if libz was available and found when + Pike was compiled. + +SEE ALSO + gz_inflate + +============================================================================ +NAME + create - initialize gzip packer + +SYNTAX + void create(int X) + or + object(Gz_deflate) Gz_deflate(int X) + +DESCRIPTION + This functionion is called when a new Gz_deflate is created. + If given, X should be a number from 0 to 9 indicating the packing / + cpu ratio. Zero means no packing, 2-3 is considered 'fast', 6 is + default and higher is considered 'slow' but gives better packing. + + This function can also be used to re-initialize a gz_deflate object + so it can be re-used. + +============================================================================ +NAME + deflate - pack data + +SYNTAX + string deflate(string data, int flush); + +DESCRIPTION + This function preforms gzip style compression on a string and + returns the packed data. Streaming can be done by calling this + functon several time and concatenating the returned data. + The optional 'flush' argument should be one f the following: + + Gz_deflate->NO_FLUSH Only data that doesn't fit in the + internal buffers is returned. + Gz_deflate->PARTIAL_FLUSH All input is packed and returned. + Gz_deflate->SYNC_FLUSH All input is packed and returned. + Packing is syncronized. + Gz_deflate->FINISH All input is packed and an 'end of + data' marker is appended. + + Using flushing will degrade packing. Normally NO_FLUSH should be + used until the end of the data when FINISH should be used. For + interactive data PARTIAL_FLUSH should be used. + +SEE ALSO + gz_inflate->inflate + +============================================================================ diff --git a/src/modules/Gz/doc/gz_inflate b/src/modules/Gz/doc/gz_inflate new file mode 100644 index 0000000000000000000000000000000000000000..153f1727d56c5459a27e69463cb6defaaba13ef1 --- /dev/null +++ b/src/modules/Gz/doc/gz_inflate @@ -0,0 +1,53 @@ +NAME + Gz_inflate - gzip unpacker + +DESCRIPTION + Gz_inflate is a builtin program written in C. It interfaces the + packing routines in the libz library. + +NOTA BENE + This program is only available if libz was available and found when + Pike was compiled. + +SEE ALSO + gz_deflate + +============================================================================ +NAME + create - initialize gzip packer + +SYNTAX + void create() + or + object(Gz_inflate) Gz_inflate() + +DESCRIPTION + This functionion is called when a new Gz_inflate is created. + It can also be called after the object has been used to re-initialize + it. + +============================================================================ +NAME + inflate - unpack data + +SYNTAX + string inflate(string data); + +DESCRIPTION + This function preforms gzip style decompression. It can inflate + a whole file at once or in blocks. + +EXAMPLES + #include <stdio.h> + // whole file + write(Gz_inflate()->inflate(stdin->read(0x7fffffff)); + + // streaming (blocks) + function inflate=Gz_inflate()->inflate; + while(string s=stdin->read(8192)) + write(inflate(s)); + +SEE ALSO + gz_deflate->deflate + +============================================================================ diff --git a/src/modules/Gz/testsuite.in b/src/modules/Gz/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..fdc03eb7f782e65352f30754796b29a963041b48 --- /dev/null +++ b/src/modules/Gz/testsuite.in @@ -0,0 +1,17 @@ +cond([[ master()->programs["/precompiled/gz_inflate"] ]], +[[ + test_true(Gz_deflate()) + test_true(Gz_deflate()->deflate) + test_true(Gz_inflate()) + test_true(Gz_inflate()->inflate) +define(dotest,[[ + test_true(Gz_deflate(1)->deflate($1)) + test_eq(Gz_inflate()->inflate(Gz_deflate(1)->deflate($1)),$1) + test_eq(Gz_inflate()->inflate(Gz_deflate(9)->deflate($1)),$1) + test_any([[object o=Gz_deflate(); return Gz_inflate()->inflate(o->deflate($1,o->PARTIAL_FLUSH) + o->deflate($1,o->FINISH))]], [[($1)+($1)]]) +]]) + dotest("") + dotest("foo") + dotest(sprintf("%'fomp'1000n")) + dotest(sprintf("%'fomp'100000n")) +]]) diff --git a/src/modules/Gz/zlib_machine.h.in b/src/modules/Gz/zlib_machine.h.in new file mode 100644 index 0000000000000000000000000000000000000000..7751a13fcd8c43413bf8bea820c72dfc7e0659ea --- /dev/null +++ b/src/modules/Gz/zlib_machine.h.in @@ -0,0 +1,13 @@ +#ifndef GMP_MACHINE_H +#define GMP_MACHINE_H + +/* Define this if you have <libz.h> */ +#undef HAVE_ZLIB_H + +/* Define this if you have -lz */ +#undef HAVE_LIBZ + +/* Define this if you have -lgz */ +#undef HAVE_LIBGZ + +#endif diff --git a/src/modules/Gz/zlibmod.c b/src/modules/Gz/zlibmod.c new file mode 100644 index 0000000000000000000000000000000000000000..d293e1f0f84f4bce51e525667c61838de93ade96 --- /dev/null +++ b/src/modules/Gz/zlibmod.c @@ -0,0 +1,373 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#include "global.h" +RCSID("$Id: zlibmod.c,v 1.1 1997/02/11 08:34:52 hubbe Exp $"); + +#include "zlib_machine.h" +#include "types.h" + +#if !defined(HAVE_LIBZ) && !defined(HAVE_LIBGZ) +#undef HAVE_ZLIB_H +#endif + +#ifdef HAVE_ZLIB_H + +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "macros.h" +#include "program.h" +#include "stralloc.h" +#include "object.h" +#include "pike_types.h" +#include "threads.h" +#include "dynamic_buffer.h" + +#include <zlib.h> + +struct zipper +{ + struct z_stream_s gz; + DEFINE_MUTEX(lock); +}; + +#define BUF 16384 + +#define THIS ((struct zipper *)(fp->current_storage)) + +static void gz_deflate_create(INT32 args) +{ + int level=Z_DEFAULT_COMPRESSION; + + if(THIS->gz.state) + { + mt_lock(& this->lock); + deflateEnd(&THIS->gz); + mt_unlock(& this->lock); + } + + if(args) + { + if(sp[-args].type != T_INT) + error("Bad argument 1 to gz->create()\n"); + if(sp[-args].u.integer < Z_NO_COMPRESSION || + sp[-args].u.integer > Z_BEST_COMPRESSION) + { + error("Compression level out of range for gz_deflate->create()\n"); + } + } + + THIS->gz.zalloc=Z_NULL; + THIS->gz.zfree=Z_NULL; + THIS->gz.opaque=THIS; + + pop_n_elems(args); + mt_lock(& THIS->lock); + level=deflateInit(&THIS->gz, level); + mt_unlock(& THIS->lock); + switch(level) + { + case Z_OK: + return; + + case Z_VERSION_ERROR: + error("libz not compatible with zlib.h!!!\n"); + break; + + default: + if(THIS->gz.msg) + error("Failed to initialize gz_deflate: %s\n",THIS->gz.msg); + else + error("Failed to initialize gz_deflate\n"); + } +} + +static int do_deflate(dynamic_buffer *buf, + struct zipper *this, + int flush) +{ + int fail=0; + + THREADS_ALLOW(); + mt_lock(& this->lock); + if(!this->gz.state) + { + fail=Z_STREAM_ERROR; + }else{ + do + { + char *loc; + int ret; + loc=low_make_buf_space(BUF,buf); + this->gz.next_out=(Bytef *)loc; + this->gz.avail_out=BUF; + ret=deflate(& this->gz, flush); + low_make_buf_space(-this->gz.avail_out,buf); + if(ret != Z_OK) + { + fail=ret; + break; + } + } while(!this->gz.avail_out || flush==Z_FINISH || this->gz.avail_in); + } + + mt_unlock(& this->lock); + THREADS_DISALLOW(); + return fail; +} + +static void gz_deflate(INT32 args) +{ + struct pike_string *data; + int flush, fail; + struct zipper *this=THIS; + dynamic_buffer buf; + + if(!THIS->gz.state) + error("gz_deflate not initialized or destructed\n"); + + initialize_buf(&buf); + + if(args<1) + error("Too few arguments to gz_deflate->deflate()\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to gz_deflate->deflate()\n"); + + data=sp[-args].u.string; + + if(args>1) + { + if(sp[1-args].type != T_INT) + error("Bad argument 2 to gz_deflate->deflate()\n"); + + flush=sp[1-args].u.integer; + + switch(flush) + { + case Z_PARTIAL_FLUSH: + case Z_FINISH: + case Z_SYNC_FLUSH: + case Z_NO_FLUSH: + break; + + defualt: + error("Argument 2 to gz_deflate->deflate() out of range.\n"); + } + }else{ + flush=Z_FINISH; + } + + this->gz.next_in=(Bytef *)data->str; + this->gz.avail_in=data->len; + + fail=do_deflate(&buf,this,flush); + pop_n_elems(args); + + if(fail != Z_OK && fail != Z_STREAM_END) + { + free(buf.s.str); + if(THIS->gz.msg) + error("Error in gz_deflate->deflate(): %s\n",THIS->gz.msg); + else + error("Error in gz_deflate->deflate(): %d\n",fail); + } + + push_string(low_free_buf(&buf)); +} + + +static void init_gz_deflate(struct object *o) +{ + mt_init(& THIS->locked); + MEMSET(& THIS->gz, 0, sizeof(THIS->gz)); + THIS->gz.zalloc=Z_NULL; + THIS->gz.zfree=Z_NULL; + THIS->gz.opaque=THIS; + deflateInit(& THIS->gz, Z_DEFAULT_COMPRESSION); +} + +static void exit_gz_deflate(struct object *o) +{ + mt_lock(& THIS->lock); + deflateEnd(&THIS->gz); + mt_unlock(& THIS->lock); +} + +/*******************************************************************/ + + +static void gz_inflate_create(INT32 args) +{ + int tmp; + if(THIS->gz.state) + { + mt_lock(this->lock); + inflateEnd(&THIS->gz); + mt_unlock(this->lock); + } + + + THIS->gz.zalloc=Z_NULL; + THIS->gz.zfree=Z_NULL; + THIS->gz.opaque=THIS; + + pop_n_elems(args); + mt_lock(THIS->lock); + tmp=inflateInit(& THIS->gz); + mt_unlock(THIS->lock); + switch(tmp) + { + case Z_OK: + return; + + case Z_VERSION_ERROR: + error("libz not compatible with zlib.h!!!\n"); + break; + + default: + if(THIS->gz.msg) + error("Failed to initialize gz_inflate: %s\n",THIS->gz.msg); + else + error("Failed to initialize gz_inflate\n"); + } +} + +static int do_inflate(dynamic_buffer *buf, + struct zipper *this, + int flush) +{ + int fail=0; + + THREADS_ALLOW(); + mt_lock(this->lock); + if(!this->gz.state) + { + fail=Z_STREAM_ERROR; + }else{ + do + { + char *loc; + int ret; + loc=low_make_buf_space(BUF,buf); + this->gz.next_out=(Bytef *)loc; + this->gz.avail_out=BUF; + ret=inflate(& this->gz, flush); + low_make_buf_space(-this->gz.avail_out,buf); + if(ret != Z_OK) + { + fail=ret; + break; + } + } while(!this->gz.avail_out || flush==Z_FINISH || this->gz.avail_in); + } + mt_unlock(this->lock); + THREADS_DISALLOW(); + return fail; +} + +static void gz_inflate(INT32 args) +{ + struct pike_string *data; + int fail; + struct zipper *this=THIS; + dynamic_buffer buf; + + if(!THIS->gz.state) + error("gz_inflate not initialized or destructed\n"); + + initialize_buf(&buf); + + if(args<1) + error("Too few arguments to gz_inflate->inflate()\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to gz_inflate->inflate()\n"); + + data=sp[-args].u.string; + + this->gz.next_in=(Bytef *)data->str; + this->gz.avail_in=data->len; + + fail=do_inflate(&buf,this,Z_PARTIAL_FLUSH); + pop_n_elems(args); + + if(fail != Z_OK && fail != Z_STREAM_END) + { + free(buf.s.str); + if(THIS->gz.msg) + error("Error in gz_inflate->inflate(): %s\n",THIS->gz.msg); + else + error("Error in gz_inflate->inflate(): %d\n",fail); + } + push_string(low_free_buf(&buf)); + if(fail != Z_STREAM_END && fail!=Z_OK && !sp[-1].u.string->len) + { + pop_stack(); + push_int(0); + } +} + +static void init_gz_inflate(struct object *o) +{ + mt_init(THIS->locked); + MEMSET(& THIS->gz, 0, sizeof(THIS->gz)); + THIS->gz.zalloc=Z_NULL; + THIS->gz.zfree=Z_NULL; + THIS->gz.opaque=0; + inflateInit(&THIS->gz); + inflateEnd(&THIS->gz); +} + +static void exit_gz_inflate(struct object *o) +{ + mt_lock(& THIS->lock); + inflateEnd(& THIS->gz); + mt_unlock(& THIS->lock); +} + +#endif + +void pike_module_exit(void) {} + +void pike_module_init(void) +{ +#ifdef HAVE_ZLIB_H + start_new_program(); + add_storage(sizeof(struct zipper)); + + add_function("create",gz_deflate_create,"function(int|void:void)",0); + add_function("deflate",gz_deflate,"function(string,int|void:string)",0); + + add_integer_constant("NO_FLUSH",Z_NO_FLUSH,0); + add_integer_constant("PARTIAL_FLUSH",Z_PARTIAL_FLUSH,0); + add_integer_constant("SYNC_FLUSH",Z_SYNC_FLUSH,0); + add_integer_constant("FINISH",Z_FINISH,0); + + set_init_callback(init_gz_deflate); + set_exit_callback(exit_gz_deflate); + + end_class("deflate",0); + + start_new_program(); + add_storage(sizeof(struct zipper)); + + add_function("create",gz_inflate_create,"function(int|void:void)",0); + add_function("inflate",gz_inflate,"function(string:string)",0); + + add_integer_constant("NO_FLUSH",Z_NO_FLUSH,0); + add_integer_constant("PARTIAL_FLUSH",Z_PARTIAL_FLUSH,0); + add_integer_constant("SYNC_FLUSH",Z_SYNC_FLUSH,0); + add_integer_constant("FINISH",Z_FINISH,0); + + set_init_callback(init_gz_inflate); + set_exit_callback(exit_gz_inflate); + + end_class("inflate",0); +#endif +} + diff --git a/src/modules/Image/.cvsignore b/src/modules/Image/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..47c84b07459ebcb78e36ee8c2e1a30cee7f440ff --- /dev/null +++ b/src/modules/Image/.cvsignore @@ -0,0 +1,8 @@ +.pure +Makefile +config.h +config.log +config.status +configure +dependencies +stamp-h diff --git a/src/modules/Image/.gitignore b/src/modules/Image/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b27587e1b501d0baa775ec479e1a69e7da31aef0 --- /dev/null +++ b/src/modules/Image/.gitignore @@ -0,0 +1,8 @@ +/.pure +/Makefile +/config.h +/config.log +/config.status +/configure +/dependencies +/stamp-h diff --git a/src/modules/Image/Makefile.in b/src/modules/Image/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..b28ff0d6bfbe5515768107fb5d5e380598a2ac1b --- /dev/null +++ b/src/modules/Image/Makefile.in @@ -0,0 +1,16 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS = image.o font.o quant.o lzw.o togif.o matrix.o pnm.o blit.o \ + pattern.o dct.o operator.o +MODNAME=image + +@dynamic_module_makefile@ + +pike: all + cd ../..; make + +pure: all + cd ../..; make pure + + +@dependencies@ diff --git a/src/modules/Image/blit.c b/src/modules/Image/blit.c new file mode 100644 index 0000000000000000000000000000000000000000..69b056dd3197272dfbf9f5746119965ac0d99e8a --- /dev/null +++ b/src/modules/Image/blit.c @@ -0,0 +1,465 @@ +/* $Id: blit.c,v 1.1 1997/02/11 08:35:41 hubbe Exp $ */ +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" +#include "threads.h" + +#include "image.h" + +extern struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) + +#if 0 +#include <sys/resource.h> +#define CHRONO(X) chrono(X) + +static void chrono(char *x) +{ + struct rusage r; + static struct rusage rold; + getrusage(RUSAGE_SELF,&r); + fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x, + r.ru_utime.tv_sec,r.ru_utime.tv_usec, + + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0) + +r.ru_utime.tv_sec-rold.ru_utime.tv_sec, + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0) + + r.ru_utime.tv_usec-rold.ru_utime.tv_usec + ); + + rold=r; +} +#else +#define CHRONO(X) +#endif + +/***************** internals ***********************************/ + +#define testrange(x) max(min((x),255),0) + +#define apply_alpha(x,y,alpha) \ + ((unsigned char)((y*(255L-(alpha))+x*(alpha))/255L)) + +#define set_rgb_group_alpha(dest,src,alpha) \ + (((dest).r=apply_alpha((dest).r,(src).r,alpha)), \ + ((dest).g=apply_alpha((dest).g,(src).g,alpha)), \ + ((dest).b=apply_alpha((dest).b,(src).b,alpha))) + +#define pixel(_img,x,y) ((_img)->img[(x)+(y)*(_img)->xsize]) + +#define setpixel(x,y) \ + (THIS->alpha? \ + set_rgb_group_alpha(THIS->img[(x)+(y)*THIS->xsize],THIS->rgb,THIS->alpha): \ + ((pixel(THIS,x,y)=THIS->rgb),0)) + +#define setpixel_test(x,y) \ + (((x)<0||(y)<0||(x)>=THIS->xsize||(y)>=THIS->ysize)? \ + 0:(setpixel(x,y),0)) + +static INLINE void getrgb(struct image *img, + INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + img->rgb.r=(unsigned char)sp[-args+args_start].u.integer; + img->rgb.g=(unsigned char)sp[1-args+args_start].u.integer; + img->rgb.b=(unsigned char)sp[2-args+args_start].u.integer; + if (args-args_start>=4) + if (sp[3-args+args_start].type!=T_INT) + error("Illegal alpha argument to %s\n",name); + else + img->alpha=sp[3-args+args_start].u.integer; + else + img->alpha=0; +} + +/*** end internals ***/ + + +void img_clear(rgb_group *dest,rgb_group rgb,INT32 size) +{ + THREADS_ALLOW(); + while (size--) *(dest++)=rgb; + THREADS_DISALLOW(); +} + +void img_box_nocheck(INT32 x1,INT32 y1,INT32 x2,INT32 y2) +{ + INT32 x,mod; + rgb_group *foo,*end,rgb; + struct image *this; + + this=THIS; + + THREADS_ALLOW(); + mod=this->xsize-(x2-x1)-1; + foo=this->img+x1+y1*this->xsize; + end=this->img+x1+y2*this->xsize; + rgb=this->rgb; + + + if (!this->alpha) + for (; foo<=end; foo+=mod) for (x=x1; x<=x2; x++) *(foo++)=rgb; + else + for (; foo<=end; foo+=mod) for (x=x1; x<=x2; x++,foo++) + set_rgb_group_alpha(*foo,rgb,this->alpha); + THREADS_DISALLOW(); +} + + +void img_blit(rgb_group *dest,rgb_group *src,INT32 width, + INT32 lines,INT32 moddest,INT32 modsrc) +{ +CHRONO("image_blit begin"); + + THREADS_ALLOW(); + while (lines--) + { + MEMCPY(dest,src,sizeof(rgb_group)*width); + dest+=moddest; + src+=modsrc; + } + THREADS_DISALLOW(); +CHRONO("image_blit end"); + +} + +void img_crop(struct image *dest, + struct image *img, + INT32 x1,INT32 y1, + INT32 x2,INT32 y2) +{ + rgb_group *new; + INT32 xp,yp,xs,ys; + + if (dest->img) { free(dest->img); dest->img=NULL; } + + if (x1>x2) x1^=x2,x2^=x1,x1^=x2; + if (y1>y2) y1^=y2,y2^=y1,y1^=y2; + + if (x1==0 && y1==0 && + img->xsize-1==x2 && img->ysize-1==y2) + { + *dest=*img; + new=malloc( (x2-x1+1)*(y2-y1+1)*sizeof(rgb_group) + 1); + if (!new) + error("Out of memory.\n"); + THREADS_ALLOW(); + MEMCPY(new,img->img,(x2-x1+1)*(y2-y1+1)*sizeof(rgb_group)); + THREADS_DISALLOW(); + dest->img=new; + return; + } + + new=malloc( (x2-x1+1)*(y2-y1+1)*sizeof(rgb_group) +1); + if (!new) + error("Out of memory.\n"); + + img_clear(new,THIS->rgb,(x2-x1+1)*(y2-y1+1)); + + dest->xsize=x2-x1+1; + dest->ysize=y2-y1+1; + + xp=max(0,-x1); + yp=max(0,-y1); + xs=max(0,x1); + ys=max(0,y1); + + if (x1<0) x1=0; else if (x1>=img->xsize) x1=img->xsize-1; + if (y1<0) y1=0; else if (y1>=img->ysize) y1=img->ysize-1; + if (x2<0) x2=0; else if (x2>=img->xsize) x2=img->xsize-1; + if (y2<0) y2=0; else if (y2>=img->ysize) y2=img->ysize-1; + + img_blit(new+xp+yp*dest->xsize, + img->img+xs+(img->xsize)*ys, + x2-x1+1, + y2-y1+1, + dest->xsize, + img->xsize); + + dest->img=new; +} + +void img_clone(struct image *newimg,struct image *img) +{ + if (newimg->img) free(newimg->img); + newimg->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize +1); + if (!newimg->img) error("Out of memory!\n"); + THREADS_ALLOW(); + MEMCPY(newimg->img,img->img,sizeof(rgb_group)*img->xsize*img->ysize); + THREADS_DISALLOW(); + newimg->xsize=img->xsize; + newimg->ysize=img->ysize; + newimg->rgb=img->rgb; +} + +void image_paste(INT32 args) +{ + struct image *img; + INT32 x1,y1,x2,y2,blitwidth,blitheight; + + if (args<1 + || sp[-args].type!=T_OBJECT + || !sp[-args].u.object + || sp[-args].u.object->prog!=image_program) + error("illegal argument 1 to image->paste()\n"); + if (!THIS->img) return; + + img=(struct image*)sp[-args].u.object->storage; + if (!img) return; + + if (args>=3) + { + if (sp[1-args].type!=T_INT + || sp[2-args].type!=T_INT) + error("illegal arguments to image->paste()\n"); + x1=sp[1-args].u.integer; + y1=sp[2-args].u.integer; + } + else x1=y1=0; + + x2=x1+img->xsize-1; + y2=y1+img->ysize-1; + + blitwidth=min(x2,THIS->xsize-1)-max(x1,0)+1; + blitheight=min(y2,THIS->ysize-1)-max(y1,0)+1; + + img_blit(THIS->img+max(0,x1)+(THIS->xsize)*max(0,y1), + img->img+max(0,-x1)+(x2-x1+1)*max(0,-y1), + blitwidth, + blitheight, + THIS->xsize, + img->xsize); + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_paste_alpha(INT32 args) +{ + struct image *img; + INT32 x1,y1,x,y; + + if (args<2 + || sp[-args].type!=T_OBJECT + || !sp[-args].u.object + || sp[-args].u.object->prog!=image_program + || sp[1-args].type!=T_INT) + error("illegal arguments to image->paste_alpha()\n"); + if (!THIS->img) return; + + img=(struct image*)sp[-args].u.object->storage; + if (!img) return; + THIS->alpha=(unsigned char)(sp[1-args].u.integer); + + if (args>=4) + { + if (sp[2-args].type!=T_INT + || sp[3-args].type!=T_INT) + error("illegal arguments to image->paste_alpha()\n"); + x1=sp[2-args].u.integer; + y1=sp[3-args].u.integer; + } + else x1=y1=0; + +/* tr�da h�r n�ndag */ + + for (x=0; x<img->xsize; x++) + for (y=0; y<img->ysize; y++) + { + THIS->rgb=pixel(img,x,y); + setpixel_test(x1+x,y1+y); + } + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_paste_mask(INT32 args) +{ + struct image *img,*mask; + INT32 x1,y1,x,y,x2,y2,smod,dmod,mmod; + rgb_group *s,*d,*m; + float q; + +CHRONO("image_paste_mask init"); + + if (args<2) + error("illegal number of arguments to image->paste_mask()\n"); + if (sp[-args].type!=T_OBJECT + || !sp[-args].u.object + || sp[-args].u.object->prog!=image_program) + error("illegal argument 1 to image->paste_mask()\n"); + if (sp[1-args].type!=T_OBJECT + || !sp[1-args].u.object + || sp[1-args].u.object->prog!=image_program) + error("illegal argument 2 to image->paste_mask()\n"); + if (!THIS->img) return; + + img=(struct image*)sp[-args].u.object->storage; + mask=(struct image*)sp[1-args].u.object->storage; + if ((!img)||(!img->img)) error("argument 1 has no image\n"); + if ((!mask)||(!mask->img)) error("argument 2 (alpha) has no image\n"); + + if (args>=4) + { + if (sp[2-args].type!=T_INT + || sp[3-args].type!=T_INT) + error("illegal coordinate arguments to image->paste_mask()\n"); + x1=sp[2-args].u.integer; + y1=sp[3-args].u.integer; + } + else x1=y1=0; + + x2=min(THIS->xsize-x1,min(img->xsize,mask->xsize)); + y2=min(THIS->ysize-y1,min(img->ysize,mask->ysize)); + +CHRONO("image_paste_mask begin"); + + s=img->img+max(0,-x1)+max(0,-y1)*img->xsize; + m=mask->img+max(0,-x1)+max(0,-y1)*mask->xsize; + d=THIS->img+max(0,-x1)+x1+(y1+max(0,-y1))*THIS->xsize; + x=max(0,-x1); + smod=img->xsize-(x2-x); + mmod=mask->xsize-(x2-x); + dmod=THIS->xsize-(x2-x); + + q=1.0/255; + + THREADS_ALLOW(); + for (y=max(0,-y1); y<y2; y++) + { + for (x=max(0,-x1); x<x2; x++) + { + if (m->r==255) d->r=s->r; + else if (m->r==0) d->r=d->r; + else d->r=(unsigned char)(((d->r*(255-m->r))+(s->r*m->r))*q); + if (m->g==255) d->g=s->g; + else if (m->g==0) d->g=d->g; + else d->g=(unsigned char)(((d->g*(255-m->g))+(s->g*m->g))*q); + if (m->b==255) d->b=s->b; + else if (m->b==0) d->b=d->b; + else d->b=(unsigned char)(((d->b*(255-m->b))+(s->b*m->b))*q); + s++; m++; d++; + } + s+=smod; m+=mmod; d+=dmod; + } + THREADS_DISALLOW(); +CHRONO("image_paste_mask end"); + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_paste_alpha_color(INT32 args) +{ + struct image *img,*mask; + INT32 x1,y1,x,y,x2,y2; + rgb_group rgb,*d,*m; + INT32 mmod,dmod; + float q; + + if (args!=1 && args!=4 && args!=6 && args!=3) + error("illegal number of arguments to image->paste_alpha_color()\n"); + if (sp[-args].type!=T_OBJECT + || !sp[-args].u.object + || sp[-args].u.object->prog!=image_program) + error("illegal argument 1 to image->paste_alpha_color()\n"); + if (!THIS->img) return; + + if (args==6 || args==4) /* colors at arg 2..4 */ + getrgb(THIS,1,args,"image->paste_alpha_color()\n"); + if (args==3) /* coords at 2..3 */ + { + if (sp[1-args].type!=T_INT + || sp[2-args].type!=T_INT) + error("illegal coordinate arguments to image->paste_alpha_color()\n"); + x1=sp[1-args].u.integer; + y1=sp[2-args].u.integer; + } + else if (args==6) /* at 5..6 */ + { + if (sp[4-args].type!=T_INT + || sp[5-args].type!=T_INT) + error("illegal coordinate arguments to image->paste_alpha_color()\n"); + x1=sp[4-args].u.integer; + y1=sp[5-args].u.integer; + } + else x1=y1=0; + + mask=(struct image*)sp[-args].u.object->storage; + if (!mask||!mask->img) error("argument 2 (alpha) has no image\n"); + + x2=min(THIS->xsize-x1,mask->xsize); + y2=min(THIS->ysize-y1,mask->ysize); + +CHRONO("image_paste_alpha_color begin"); + + m=mask->img+max(0,-x1)+max(0,-y1)*mask->xsize; + d=THIS->img+max(0,-x1)+x1+(y1+max(0,-y1))*THIS->xsize; + x=max(0,-x1); + mmod=mask->xsize-(x2-x); + dmod=THIS->xsize-(x2-x); + + q=1.0/255; + + rgb=THIS->rgb; + + THREADS_ALLOW(); + for (y=max(0,-y1); y<y2; y++) + { + for (x=max(0,-x1); x<x2; x++) + { + if (m->r==255) d->r=rgb.r; + else if (m->r==0) ; + else d->r=(unsigned char)(((d->r*(255-m->r))+(rgb.r*m->r))*q); + if (m->g==255) d->g=rgb.g; + else if (m->g==0) ; + else d->g=(unsigned char)(((d->g*(255-m->g))+(rgb.g*m->g))*q); + if (m->b==255) d->b=rgb.b; + else if (m->b==0) ; + else d->b=(unsigned char)(((d->b*(255-m->b))+(rgb.b*m->b))*q); + m++; d++; + } + m+=mmod; d+=dmod; + } + THREADS_DISALLOW(); +CHRONO("image_paste_alpha_color end"); + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void img_box(INT32 x1,INT32 y1,INT32 x2,INT32 y2) +{ + if (x1>x2) x1^=x2,x2^=x1,x1^=x2; + if (y1>y2) y1^=y2,y2^=y1,y1^=y2; + if (x2<0||y2<0||x1>=THIS->xsize||y1>=THIS->ysize) return; + img_box_nocheck(max(x1,0),max(y1,0),min(x2,THIS->xsize-1),min(y2,THIS->ysize-1)); +} + + + diff --git a/src/modules/Image/config.h.in b/src/modules/Image/config.h.in new file mode 100644 index 0000000000000000000000000000000000000000..573e31d4ca89134b34b9caaabc21aaf9f392fcad --- /dev/null +++ b/src/modules/Image/config.h.in @@ -0,0 +1,19 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define if you have the getpagesize function. */ +#undef HAVE_GETPAGESIZE + +/* Define if you have the <fcntl.h> header file. */ +#undef HAVE_FCNTL_H + +/* Define if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the <sys/fcntl.h> header file. */ +#undef HAVE_SYS_FCNTL_H + +/* Define if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H diff --git a/src/modules/Image/configure.in b/src/modules/Image/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..4df99cce8ce80f712f8a1b3dc45eefb9111abc27 --- /dev/null +++ b/src/modules/Image/configure.in @@ -0,0 +1,14 @@ +AC_INIT(image.c) +AC_CONFIG_HEADER(config.h) + +sinclude(../module_configure.in) + +AC_FUNC_MMAP + +AC_CHECK_HEADERS(sys/fcntl.h fcntl.h stdlib.h) + +AC_SUBST(RANLIB) + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) + + diff --git a/src/modules/Image/dct.c b/src/modules/Image/dct.c new file mode 100644 index 0000000000000000000000000000000000000000..7bd39bd4b4049c53a5efbe54b858c2cb819242ae --- /dev/null +++ b/src/modules/Image/dct.c @@ -0,0 +1,165 @@ +/* $Id: dct.c,v 1.1 1997/02/11 08:35:42 hubbe Exp $ */ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" + +#include "image.h" + +extern struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) +#define testrange(x) max(min((x),255),0) + +static const double c0=0.70710678118654752440; +static const double pi=3.14159265358979323846; + +void image_dct(INT32 args) +{ + rgbd_group *area,*val; + struct object *o; + struct image *img; + INT32 x,y,u,v; + double xsz2,ysz2,enh,xp,yp,dx,dy; + double *costbl; + rgb_group *pix; + + if (!THIS->img) error("no image\n"); + + fprintf(stderr,"%d bytes, %d bytes\n", + sizeof(rgbd_group)*THIS->xsize*THIS->ysize, + sizeof(rgb_group)*THIS->xsize*THIS->ysize+1); + + if (!(area=malloc(sizeof(rgbd_group)*THIS->xsize*THIS->ysize+1))) + error("Out of memory\n"); + + if (!(costbl=malloc(sizeof(double)*THIS->xsize+1))) + { + free(area); + error("Out of memory\n"); + } + + o=clone(image_program,0); + img=(struct image*)(o->storage); + *img=*THIS; + + if (args>=2 + && sp[-args].type==T_INT + && sp[1-args].type==T_INT) + { + img->xsize=max(1,sp[-args].u.integer); + img->ysize=max(1,sp[1-args].u.integer); + } + else error("Illegal arguments to image->dct()\n"); + + if (!(img->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize+1))) + { + free(area); + free(costbl); + free_object(o); + error("Out of memory\n"); + } + + xsz2=THIS->xsize*2.0; + ysz2=THIS->ysize*2.0; + + enh=(8.0/THIS->xsize)*(8.0/THIS->ysize); + + for (u=0; u<THIS->xsize; u++) + { + double d,z0; + rgbd_group sum; + + for (v=0; v<THIS->ysize; v++) + { + d=(u?1:c0)*(v?1:c0)/4.0; + sum.r=sum.g=sum.b=0; + pix=THIS->img; + + for (x=0; x<THIS->xsize; x++) + costbl[x]=cos( (2*x+1)*u*pi/xsz2 ); + + for (y=0; y<THIS->ysize; y++) + { + z0=cos( (2*y+1)*v*pi/ysz2 ); + for (x=0; x<THIS->xsize; x++) + { + double z; + z = costbl[x] * z0; + sum.r+=pix->r*z; + sum.g+=pix->g*z; + sum.b+=pix->b*z; + pix++; + } + } + sum.r*=d; + sum.g*=d; + sum.b*=d; + area[u+v*THIS->xsize]=sum; + } + fprintf(stderr,"."); fflush(stderr); + } + fprintf(stderr,"\n"); + + dx=((double)(THIS->xsize-1))/(img->xsize); + dy=((double)(THIS->ysize-1))/(img->ysize); + + pix=img->img; + for (y=0,yp=0; y<img->ysize; y++,yp+=dy) + { + double z0; + rgbd_group sum; + + for (x=0,xp=0; x<img->xsize; x++,xp+=dx) + { + sum.r=sum.g=sum.b=0; + val=area; + + for (u=0; u<THIS->xsize; u++) + costbl[u]=cos( (2*xp+1)*u*pi/xsz2 ); + + for (v=0; v<THIS->ysize; v++) + { + z0=cos( (2*yp+1)*v*pi/ysz2 )*(v?1:c0)/4.0; + for (u=0; u<THIS->xsize; u++) + { + double z; + z = (u?1:c0) * costbl[u] * z0; + sum.r+=val->r*z; + sum.g+=val->g*z; + sum.b+=val->b*z; + val++; + } + } + sum.r*=enh; + sum.g*=enh; + sum.b*=enh; + pix->r=testrange(((int)(sum.r+0.5))); + pix->g=testrange(((int)(sum.g+0.5))); + pix->b=testrange(((int)(sum.b+0.5))); + pix++; + } + fprintf(stderr,"."); fflush(stderr); + } + + free(area); + free(costbl); + + pop_n_elems(args); + push_object(o); +} diff --git a/src/modules/Image/doc.txt b/src/modules/Image/doc.txt new file mode 100644 index 0000000000000000000000000000000000000000..3f3cc2b2c97015454379ad1abd3bfff16f6abec5 --- /dev/null +++ b/src/modules/Image/doc.txt @@ -0,0 +1,487 @@ + Pike module: + + image + + Pontus Hagland law@infovav.se + Per Hedbor per@infovav.se + David K�gedal kg@infovav.se +---------------------------------------------------------------------------- +This package adds two Pike progams: + + * "precompiled/image" and + * "precompiled/font". + +---------------------------------------------------------------------------- + +methods in precompiled/image: + +Methods resulting in a new object: +object clone( [int xsize,int ysize [,int r,int g,int b] ] ); + +object copy( [int x1,int y1,int x2,int y2 [,int r,int g,int b] ] ); +object autocrop( [int border_width [,int left,int right,int top,int bottom] +[,int r,int g,int b] ] ); + +object gray(); +object color(int r,int g,int b); +object invert(); + +object mirrorx(void); +object mirrory(void); +object rotate_cw(void); +object rotate_ccw(void); +object threshold([int r,int g,int b]); +object apply_matrix(array(array(int)) matrix,[int r,int g,int b[,int div]]); + +object scale(float factor); +object scale(float factorx,float factory); +object scale(int newx|0,int newy|0); + +Methods operating on current object: +string toppm(void); +string|object fromppm(string s); +string togif( [int r,inr g,int b] ); + +object paste(object img [,int x,int y]) +object paste_alpha(object img, int alpha [,int x, int y]); +object paste_mask(object img, object alpha_mask [,int x,int y]); + +object setcolor(int r,int g,int b); +object setpixel(int x,int y [,int r,int g,int b] ); +object line(int x1,int y1,int x2,int y2 [,int r,int g,int b] ); +object box(int x1,int y1,int x2,int y2 [,int r,int g,int b] ); +object circle(int x,int y,int radx,int rady [,int r,int b,int g] ); +object tuned_box(int x1,int y1,int x2,int y2,array(array(int)) corner_rgb); + +Information giving methods: +object xsize(); +object ysize(); +---------------------------------------------------------------------------- + +METHOD + object apply_matrix(array(array(int)) matrix,[int r,int g,int b[,int + div]]); +DESCRIPTION + This method applies a matrix on the image. Each surrounding pixel is + multiplied with the value of the matrix element in that point, these + values are added and divided by the total sum of the matrix values (and + the div argument) and stored on the pixel (eventually added to the + r,g,b argument given as 'mean' value). + + It is possible to use a matrix of RGB groups (ie an array of three + integers) instead of the simple values, this making it possible to + apply different matrices on red, green and blue channel. +RETURN VALUE + the new object +EXAMPLE + A 'blur' operation (3x3, gaussian): + + blurred=image->apply_matrix( ({ ({1,2,1}), ({2,3,2}), ({1,2,1}) }) ); + + A 'Emboss' operation (3x3): + + emossed=image->apply_matrix(({ ({0,1,8}), ({-1,0,1}), ({-8,-1,0}) }), 128,128,128, 15 ); + + Here i'm using 128,128,128 (gray) as a mean, because i get negative + values. + A division by 15 is good to give 'normal' edges. +BUGS + not known + +---------------------------------------------------------------------------- + +METHOD + object autocrop( [int border_width [,int left,int right,int top,int + bottom] [,int r,int g,int b] ] ); +DESCRIPTION + Crops away unneccesary borders from the image. The border argument is + to define the new thickness of the surrounding border and the r,g,b is + the newly created border color. + + The left, right, ... arguments is used to tell which edges should be + autocropped. +RETURN VALUE + the new object +EXAMPLE + + cropped=image->autocrop(); + +BUGS + now known + +---------------------------------------------------------------------------- + +METHOD + object box(int x1,int y1,int x2,int y2 [,int r,int g,int b] ); +DESCRIPTION + Draw a box of the default or specified color. +RETURN VALUE + the image object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object circle(int x,int y,int radx,int rady [,int r,int b,int g] ); +DESCRIPTION + Draw a circle. The coordinates given are the center of the image and + the radius in x (horisontal) and y (vertical), this making it possible + to draw an ellipse too. :-) +RETURN VALUE + the image object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object clone( [int xsize,int ysize [,int r,int g,int b] ] ); +DESCRIPTION + make a new object and return it + o no arguments -> old image is copied + o size is given -> old image is copied cropped + o color is given -> new default color +RETURN VALUE + the new object +SEE ALSO + copy, clear +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object color(int r,int g,int b); +DESCRIPTION + Apply a color filter on the image. +RETURN VALUE + the new object +EXAMPLE + + cyan=image->color(64,255,192); + + This function is most usable on a image that has been grayed first. +BUGS + +---------------------------------------------------------------------------- + +METHOD + object copy( [int x1,int y1,int x2,int y2 [,int r,int g,int b] ] ); +DESCRIPTION + Make a copy, or a copy of a part of the image. It is possible to copy + more then the image, to extend the image, this area is filled with the + current (or given) color. +RETURN VALUE + the new image object +EXAMPLE + + copy=image->copy(); + + copy=image->copy(-10,-10,image->xsize()+9,image->ysize()+9); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + string|object fromppm(string s); +DESCRIPTION + Import a ppm image. +RETURN VALUE + 0 (object) upon success, else the error message (string). +EXAMPLE + + image=clone( (program)"precompiled/image" ); + image->fromppm(read_bytes("my_image.ppm",0,10000000)); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object gray([int r,int g,int b]); +DESCRIPTION + Make this image gray (each r,g,b gets the same value). + If a color is given, that specifies the amount of r, g, and b that is + used to compute the gray level. Default is 87,127,41. +RETURN VALUE + the new object +EXAMPLE + + gray=image->gray() + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object invert(); +DESCRIPTION + Invert the image. +RETURN VALUE + the new object +EXAMPLE + + inverted=image->invert() + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object line(int x1,int y1,int x2,int y2 [,int r,int g,int b] ); +DESCRIPTION + Draw a line from x1,y1 to x2,y2. +RETURN VALUE + the image object +EXAMPLE + + image->line(17,100,42,1000); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object mirrorx(void); + object mirrory(void); +DESCRIPTION + Mirrors the image, horisontally or vertically. +RETURN VALUE + the new image object +EXAMPLE + + mirrored=image->mirrorx(); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object rotate_cw(void); + object rotate_ccw(void); +DESCRIPTION + Rotate the image, clockwise or counterclockwise, 90 degrees. + This operation is very fast compared to rotating any angle. +RETURN VALUE + the new image object +EXAMPLE + + snurr=image->rotate_cw(); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object paste(object img [,int x,int y]) + object paste_alpha(object img, int alpha [,int x, int y]); + object paste_mask(object img, object alpha_mask [,int x,int y]); +DESCRIPTION + Paste an image on this image. Use the specified alpha channel value or + the second specified image as an alpha channel. + The first argument is the image that will be pasted. +RETURN VALUE + the image object this function doesn't return anything +EXAMPLE + + image->paste(other_smaller_image,17,42); + + image->paste_mask(other_image,alpha_channel_image); + + Paste a dog on a landscape: + + landscape->paste(dog,dog_alpha_channel,xpos,ypos); + + Write some text: + + text=font->write("some text"); + foreground=text->clear(255,255,255); // white + background->paste(foreground,text,xpos,ypos); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object scale(float factor); (1 + object scale(float factorx,float factory); (2 + object scale(int newx|0,int newy|0); (3 +DESCRIPTION + Scale this image. + 1. scale the image with a (line scale) factor + 2. scale the image with different factors on x and y + 3. scale the image to a new size + with newx or newy set to zero, just scale the image to fit the x + or y size and keep proportions. +RETURN VALUE + the new object this function doesn't return anything +EXAMPLE +BUGS + +---------------------------------------------------------------------------- +METHOD + object setcolor(int r,int g,int b); +DESCRIPTION + set the default color used for drawing lines, etc +RETURN VALUE + the image object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- +METHOD + object setpixel(int x,int y [,int r,int g,int b] ); +DESCRIPTION + set the color of the specified pixel +RETURN VALUE + the image object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object threshold([int r,int g,int b]); +DESCRIPTION + make image black-and-white using the given value as the threshold +RETURN VALUE + the new object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + string togif( [int r,inr g,int b] ); +DESCRIPTION + export gif + if the color are given, this is the transparent color +RETURN VALUE + the gifimage as a string +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + string toppm(object); +DESCRIPTION + export ppm +RETURN VALUE + the ppm image as a string +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object tuned_box(int x1,int y1,int x2,int y2,array(array(int)) + corner_rgb); +DESCRIPTION + draw a box with the specified corner colours, and shade the colors + between +RETURN VALUE + the image object +EXAMPLE + + image->tuned_box(0,0,img->xsize()-1,img->ysize()-1, + ({({0,0,64}),({16,16,128}), + ({16,16,128}),({192,160,128})})); + +BUGS + +---------------------------------------------------------------------------- + +METHOD + object xsize(); + object ysize(); +DESCRIPTION +RETURN VALUE + Gives the x- or the y-size (horisontal or vertical size) of the image. +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +methods in precompiled/font: + +int load(string file_name); +object write(string line, ...); +---------------------------------------------------------------------------- + +METHOD + int load(string file_name); +DESCRIPTION + load this font object with the font from the specified file +RETURN VALUE + true on success +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +METHOD + object write(string line, ...); +DESCRIPTION + make a new image object from the specified text, each argument + representing a line +RETURN VALUE + the new image object +EXAMPLE +BUGS + +---------------------------------------------------------------------------- + +Example program: + +(pike) + +int main() +{ + object txt,o,shad,font; + int i; + + txt = + (font=clone((program)"/precompiled/font")) + ->load("/usr/local/lib/pike/fonts/64/helvetica_bold_r") + ->write("The Image Module") + ->autocrop(20,0,0,0); + + shad=txt->mirrory()->scale(1.0,0.3)->color(64,64,64); + + o=clone((program)"/precompiled/image", + txt->xsize(),txt->ysize(), 0,0,100) + ->tuned_box(0,0,txt->xsize(),txt->ysize(), + ({({0,0,0}),({0,0,0}), + ({0,0,255}),({128,128,0})})); + + o->setcolor(255,255,255,200); + for (i=0; i<30; i++) + if (random(2)) + o->line(random(o->xsize()),o->ysize()-10-random(20+i*3), + o->xsize()-1-random(30),o->ysize()-1); + else + o->line(random(o->xsize()),o->ysize()-10-random(20+i), + random(30),o->ysize()-1); + + for (i=0; i<10; i++) + o->box(random(o->xsize()),random(o->ysize()), + random(o->xsize()),random(o->ysize()), + random(256),random(256),random(256),220); + + o -> paste_mask(txt->clear(0,255,0), + shad,0,(int)(font->baseline()*0.7)+shad->ysize()-10) + -> paste_mask(txt->clear(255,255,0), + txt->apply_matrix(({({1,2,1}),({2,4,2}),({1,2,1})})) + ->apply_matrix(({({1,2,1}),({2,4,2}),({1,2,1})})) + ->modify_by_intensity(1,0,0, 0,255,255,255,255,255)) + -> paste_mask(txt->clone() + ->tuned_box(0,0,txt->xsize()-1,txt->ysize()-1, + ({({128,128,128}),({64,128,0}), + ({64,128,0}),({255,255,0})})), + txt); + write(o->togif_fs()); + return 0; +} diff --git a/src/modules/Image/doc/image.html b/src/modules/Image/doc/image.html new file mode 100644 index 0000000000000000000000000000000000000000..10dca57176c0858f7754b0d1aa597b9fac189827 --- /dev/null +++ b/src/modules/Image/doc/image.html @@ -0,0 +1,568 @@ +<!-- $id$ --> + +<center> +<i>Pike module: </i> +<h1>image</h1> +Pontus Hagland <a href=law@infovav.se><i>law@infovav.se</i></a> +<br>Per Hedbor <a href=per@infovav.se><i>per@infovav.se</i></a> +<br>David K�gedal <a href=kg@infovav.se><i>kg@infovav.se</i></a> +</center> + +<hr> + +This package adds two Pike progams: + +<ul> +<li><tt><a href=#image>"precompiled/image"</a></tt> and +<li><tt><a href=#font>"precompiled/font"</a></tt>. +</ul> + +<hr> + +<a name=image><h2>methods in precompiled/image:</h2></a> + +Methods resulting in a new object: + +<br><a name=cloner><tt>object <a href=#clone><b>clone</b></a>( </tt>[<tt>int <b>xsize</b>,int <b>ysize</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<p><a name=copyr><tt>object <a href=#copy><b>copy</b></a>( </tt>[<tt>int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<br><a name=autocropr><tt>object <a href=#autocrop><b>autocrop</b></a>( </tt>[<tt>int <b>border_width</b> </tt>[<tt>,int <b>left</b>,int <b>right</b>,int <b>top</b>,int <b>bottom</b></tt>]<tt></b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<p><a name=grayr><tt>object <a href=#gray><b>gray</b></a>();</tt></a> +<br><a name=colorr><tt>object <a href=#color><b>color</b></a>(int <b>r</b>,int <b>g</b>,int <b>b</b>);</tt></a> +<br><a name=invertr><tt>object <a href=#invert><b>invert</b></a>();</tt></a> + +<p><a name=mirrorxr><tt>object <a href=#mirrorx><b>mirrorx</b></a>(void);</tt></a> +<br><a name=mirroryr><tt>object <a href=#mirrory><b>mirrory</b></a>(void);</tt></a> +<br><a name=rotate_cwr><tt>object <a href=#rotate_cw><b>rotate_cw</b></a>(void);</tt></a> +<br><a name=rotate_ccwr><tt>object <a href=#rotate_ccw><b>rotate_ccw</b></a>(void);</tt></a> +<br><a name=thresholdr><tt>object <a href=#threshold><b>threshold</b></a>(</tt>[<tt>int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt></b>); </tt></a> +<br><a name=apply_matrixr><tt>object <a href=#apply_matrix><b>apply_matrix</b></a>(array(array(int)) <b>matrix</b>,</tt>[<tt>int <b>r</b>,int <b>g</b>,int <b>b</b></tt>[<tt>,int <b>div</b></tt>]]<tt>);</tt></a> +<p><a name=scaler><tt>object <a href=#scale><b>scale</b></a>(float <b>factor</b>);</tt></a> +<br><a name=scaler><tt>object <a href=#scale><b>scale</b></a>(float <b>factorx</b>,float <b>factory</b>);</tt></a> +<br><a name=scaler><tt>object <a href=#scale><b>scale</b></a>(int <b>newx</b></tt>|<tt><b>0</b>,int <b>newy</b></tt>|<tt><b>0</b>);</tt></a> + +<p>Methods operating on current object: + +<br><a name=toppmr><tt>string <a href=#toppm><b>toppm</b></a>(void</b>);</tt></a> +<br><a name=fromppmr><tt>string|object <a href=#fromppm><b>fromppm</b></a>(string <b>s</b>);</tt></a> +<br><a name=togifr><tt>string <a href=#togif><b>togif</b></a>( </tt>[<tt>int <b>r</b>,inr g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<p><a name=paster><tt>object <a href=#paste><b>paste</b></a>(object <b>img</b> </tt>[<tt>,int <b>x</b>,int <b>y</b></tt>]<tt></b>)</tt></a> +<br><a name=paste_alphar><tt>object <a href=#paste_alpha><b>paste_alpha</b></a>(object <b>img</b>, int <b>alpha</b> </tt>[<tt>,int <b>x</b>, int <b>y</b></tt>]<tt></b>);</tt></a> +<br><a name=paste_maskr><tt>object <a href=#paste_mask><b>paste_mask</b></a>(object <b>img</b>, object <b>alpha_mask</b> </tt>[<tt>,int <b>x</b>,int <b>y</b></tt>]<tt></b>);</tt></a> +<p><a name=setcolorr><tt>object <a href=#setcolor><b>setcolor</b></a>(int <b>r</b>,int <b>g</b>,int <b>b</b>);</tt></a> +<br><a name=setpixelr><tt>object <a href=#setpixel><b>setpixel</b></a>(int <b>x</b>,int <b>y</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<br><a name=liner><tt>object <a href=#line><b>line</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<br><a name=boxr><tt>object <a href=#box><b>box</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<br><a name=circler><tt>object <a href=#circle><b>circle</b></a>(int <b>x</b>,int <b>y</b>,int <b>radx</b>,int <b>rady</b> </tt>[<tt>,int <b>r</b>,int <b>b</b>,int <b>g</b></tt>]<tt> </b>);</tt></a> +<br><a name=tuned_boxr><tt>object <a href=#tuned_box><b>tuned_box</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b>,array(array(int)) corner_rgb</b>);</tt></a> + +<p>Information giving methods: +<br><a name=xsizer><tt>object <a href=#xsize><b>xsize</b></a>();</tt></a> +<br><a name=ysizer><tt>object <a href=#ysize><b>ysize</b></a>();</tt></a> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=apply_matrix><tt>object <a href=#apply_matrixr><b>apply_matrix</b></a>(array(array(int)) <b>matrix</b>,</tt>[<tt>int <b>r</b>,int <b>g</b>,int <b>b</b></tt>[<tt>,int <b>div</b></tt>]]<tt>);</tt></a> +<dt>DESCRIPTION + +<dd>This method applies a matrix on the image. Each surrounding pixel +is multiplied with the value of the matrix element in that point, +these values are added and divided by the total sum of the matrix +values (and the <b>div</b> argument) and stored on the pixel +(eventually added to the <b>r</b>,<b>g</b>,<b>b</b> argument given as +'mean' value). + +<p>It is possible to use a matrix of RGB groups (ie an array of three +integers) instead of the simple values, this making it possible to +apply different matrices on red, green and blue channel. + +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd> +A 'blur' operation (3x3, gaussian): +<pre>blurred=image->apply_matrix( ({ ({1,2,1}), ({2,3,2}), ({1,2,1}) }) );</pre> +<p>A 'Emboss' operation (3x3): +<pre>emossed=image->apply_matrix(({ ({0,1,8}), ({-1,0,1}), ({-8,-1,0}) }), 128,128,128, 15 );</pre> +Here i'm using 128,128,128 (gray) as a mean, because i get negative values. +<br>A division by 15 is good to give 'normal' edges. + +<dt>BUGS +<dd>not known +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=autocrop><tt>object <a href=#autocropr><b>autocrop</b></a>( </tt>[<tt>int <b>border_width</b> </tt>[<tt>,int <b>left</b>,int <b>right</b>,int <b>top</b>,int <b>bottom</b></tt>]<tt></b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION + +<dd>Crops away unneccesary borders from the image. The <b>border</b> +argument is to define the new thickness of the surrounding border and +the <b>r</b>,<b>g</b>,<b>b</b> is the newly created border color. + +<p>The <b>left</b>, <b>right</b>, ... arguments is used to tell which +edges should be autocropped. + +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd><pre>cropped=image->autocrop();</pre> +<dt>BUGS +<dd>now known +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=box><tt>object <a href=#boxr><b>box</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION + +<dd>Draw a box of the default or specified color. + +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=circle><tt>object <a href=#circler><b>circle</b></a>(int <b>x</b>,int <b>y</b>,int <b>radx</b>,int <b>rady</b> </tt>[<tt>,int <b>r</b>,int <b>b</b>,int <b>g</b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>Draw a circle. The coordinates given are the center of the image and the radius in x (horisontal) and y (vertical), this making it possible to draw an ellipse too. <tt>:-)</tt> +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=clone><tt>object <a href=#cloner><b>clone</b></a>( </tt>[<tt>int <b>xsize</b>,int <b>ysize</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>make a new object and return it +<ul> +<li>no arguments -> old image is copied +<li>size is given -> old image is copied cropped +<li>color is given -> new default color +</ul> +<dt>RETURN VALUE +<dd>the new object +<dt>SEE ALSO +<dd><a href=#copy>copy</a>, <a href=#clear>clear</a> +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=color><tt>object <a href=#colorr><b>color</b></a>(int <b>r</b>,int <b>g</b>,int <b>b</b>);</tt></a> +<dt>DESCRIPTION +<dd>Apply a color filter on the image. +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd><pre>cyan=image->color(64,255,192);</pre> +This function is most usable on a image that has been <a href=#gray>gray</a>ed first. +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=copy><tt>object <a href=#copyr><b>copy</b></a>( </tt>[<tt>int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>Make a copy, or a copy of a part of the image. + It is possible to copy more then the image, to extend the image, + this area is filled with the current (or given) color. +<dt>RETURN VALUE +<dd>the new image object +<dt>EXAMPLE +<dd><pre>copy=image->copy(); + +copy=image->copy(-10,-10,image->xsize()+9,image->ysize()+9);</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=fromppm><tt>string|object <a href=#fromppmr><b>fromppm</b></a>(string <b>s</b>);</tt></a> +<dt>DESCRIPTION +<dd>Import a ppm image. +<dt>RETURN VALUE +<dd>0 (object) upon success, else the error message (string). +<dt>EXAMPLE +<dd><pre>image=clone( (program)"precompiled/image" ); +image->fromppm(read_bytes("my_image.ppm",0,10000000));</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=gray><tt>object <a href=#grayr><b>gray</b></a>(</tt>[<tt>int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt>);</tt></a> +<dt>DESCRIPTION + +<dd>Make this image gray (each r,g,b gets the same value).<br> If a +color is given, that specifies the amount of r, g, and b that is used +to compute the gray level. Default is 87,127,41. + +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd><pre>gray=image->gray()</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=invert><tt>object <a href=#invertr><b>invert</b></a>();</tt></a> +<dt>DESCRIPTION +<dd>Invert the image. +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd><pre>inverted=image->invert()</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=line><tt>object <a href=#liner><b>line</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>Draw a line from <b>x1</b>,<b>y1</b> to <b>x2</b>,<b>y2</b>. +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd><pre>image->line(17,100,42,1000);</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=mirrorx><tt>object <a href=#mirrorxr><b>mirrorx</b></a>(void);</tt></a> +<dd><a name=mirrory><tt>object <a href=#mirroryr><b>mirrory</b></a>(void);</tt></a> +<dt>DESCRIPTION +<dd>Mirrors the image, horisontally or vertically. +<dt>RETURN VALUE +<dd>the new image object +<dt>EXAMPLE +<dd><pre>mirrored=image->mirrorx();</pre> +<dt>BUGS +<dd> +</dl> + +<hr> +<dl> +<dt>METHOD +<dd><a name=rotate_cw><tt>object <a href=#rotate_cwr><b>rotate_cw</b></a>(void);</tt></a> +<dd><a name=rotate_ccw><tt>object <a href=#rotate_ccwr><b>rotate_ccw</b></a>(void);</tt></a> +<dt>DESCRIPTION +<dd>Rotate the image, clockwise or counterclockwise, 90 degrees. +<br>This operation is very fast compared to rotating any angle. +<dt>RETURN VALUE +<dd>the new image object +<dt>EXAMPLE +<dd><pre>snurr=image->rotate_cw();</pre> +<dt>BUGS +<dd> +</dl> +<hr> + +<dl> +<dt>METHOD +<dd><a name=paste><tt>object <a href=#paster><b>paste</b></a>(object <b>img</b> </tt>[<tt>,int <b>x</b>,int <b>y</b></tt>]<tt></b>)</tt></a> +<dd><a name=paste_alpha><tt>object <a href=#paste_alphar><b>paste_alpha</b></a>(object <b>img</b>, int <b>alpha</b> </tt>[<tt>,int <b>x</b>, int <b>y</b></tt>]<tt></b>);</tt></a> +<dd><a name=paste_mask><tt>object <a href=#paste_maskr><b>paste_mask</b></a>(object <b>img</b>, object <b>alpha_mask</b> </tt>[<tt>,int <b>x</b>,int <b>y</b></tt>]<tt></b>);</tt></a> +<dt>DESCRIPTION +<dd>Paste an image on this image. Use the specified alpha channel +value or the second specified image as an alpha channel.<br> +The first argument is the image that will be pasted. +<dt>RETURN VALUE +<dd>the image object +this function doesn't return anything +<dt>EXAMPLE +<dd><pre>image->paste(other_smaller_image,17,42); + +image->paste_mask(other_image,alpha_channel_image);</pre> +Paste a dog on a landscape:<pre>landscape->paste(dog,dog_alpha_channel,xpos,ypos);</pre> +Write some text:<pre>text=font->write("some text"); +foreground=text->clear(255,255,255); // white +background->paste(foreground,text,xpos,ypos);</pre> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=scale><tt>object <a href=#scaler><b>scale</b></a>(float <b>factor</b>);</tt> (1</a> +<dd><a name=scale><tt>object <a href=#scaler><b>scale</b></a>(float <b>factorx</b>,float <b>factory</b>);</tt> (2</a> +<dd><a name=scale><tt>object <a href=#scaler><b>scale</b></a>(int <b>newx</b></tt>|<tt><b>0</b>,int <b>newy</b></tt>|<tt><b>0</b>);</tt> (3</a> +<dt>DESCRIPTION +<dd>Scale this image. +<ol> +<li>scale the image with a (line scale) factor +<li>scale the image with different factors on x and y +<li>scale the image to a new size +<br>with newx or newy set to zero, just scale the image to fit the x +or y size and keep proportions. +</ol> +<dt>RETURN VALUE +<dd>the new object +this function doesn't return anything +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dt>METHOD +<dd><a name=setcolor><tt>object <a href=#setcolorr><b>setcolor</b></a>(int <b>r</b>,int <b>g</b>,int <b>b</b>);</tt></a> +<dt>DESCRIPTION +<dd>set the default color used for drawing lines, etc +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dt>METHOD +<dd><a name=setpixel><tt>object <a href=#setpixelr><b>setpixel</b></a>(int <b>x</b>,int <b>y</b> </tt>[<tt>,int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>set the color of the specified pixel +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=threshold><tt>object <a href=#thresholdr><b>threshold</b></a>(</tt>[<tt>int <b>r</b>,int <b>g</b>,int <b>b</b></tt>]<tt></b>); </tt></a> +<dt>DESCRIPTION +<dd>make image black-and-white using the given value as the threshold +<dt>RETURN VALUE +<dd>the new object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=togif><tt>string <a href=#togifr><b>togif</b></a>( </tt>[<tt>int <b>r</b>,inr g</b>,int <b>b</b></tt>]<tt> </b>);</tt></a> +<dt>DESCRIPTION +<dd>export gif +<br>if the color are given, this is the transparent color +<dt>RETURN VALUE +<dd>the gifimage as a string +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=toppm><tt>string <a href=#toppmr><b>toppm</b></a>(object</b>);</tt></a> +<dt>DESCRIPTION +<dd>export ppm +<dt>RETURN VALUE +<dd>the ppm image as a string +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=tuned_box><tt>object <a href=#tuned_boxr><b>tuned_box</b></a>(int <b>x1</b>,int <b>y1</b>,int <b>x2</b>,int <b>y2</b>,array(array(int)) corner_rgb</b>);</tt></a> +<dt>DESCRIPTION +<dd>draw a box with the specified corner colours, and shade the colors between +<dt>RETURN VALUE +<dd>the image object +<dt>EXAMPLE +<dd><pre>image->tuned_box(0,0,img->xsize()-1,img->ysize()-1, + ({({0,0,64}),({16,16,128}), + ({16,16,128}),({192,160,128})}));</pre> +<dt>BUGS +<dd> +</dl> + +<hr> +<dl> +<dt>METHOD +<dd><a name=xsize><tt>object <a href=#xsizer><b>xsize</b></a>();</tt></a> +<dd><a name=ysize><tt>object <a href=#ysizer><b>ysize</b></a>();</tt></a> +<dt>DESCRIPTION +<dd> +<dt>RETURN VALUE +<dd>Gives the x- or the y-size (horisontal or vertical size) of the image. +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + + +<hr> + +<a name=font><h2>methods in precompiled/font:</h2></a> + +<a name=loadr><tt>int <a href=#load><b>load</b></a>(string <b>file_name</b>);</tt> +<br><a name=writer><tt>object <a href=#write><b>write</b></a>(string <b>line</b>, </tt>...<tt>);</tt> + +<hr> +<dl> +<dt>METHOD +<dd><a name=load><tt>int <a href=#loadr><b>load</b></a>(string <b>file_name</b>);</tt> +<dt>DESCRIPTION +<dd>load this font object with the font from the specified file +<dt>RETURN VALUE +<dd>true on success +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<dl> +<dt>METHOD +<dd><a name=write><tt>object <a href=#writer><b>write</b></a>(string <b>line</b>, </tt>...<tt>);</tt> +<dt>DESCRIPTION +<dd>make a new image object from the specified text, each argument representing a line +<dt>RETURN VALUE +<dd>the new image object +<dt>EXAMPLE +<dd> +<dt>BUGS +<dd> +</dl> + +<hr> + +<h2>Example program:</h2> +(pike) +<pre> + +int main() +{ + object txt,o,shad,font; + int i; + + txt = + (font=clone((program)"/precompiled/font")) + ->load("/usr/local/lib/pike/fonts/64/helvetica_bold_r") + ->write("The Image Module") + ->autocrop(20,0,0,0); + + shad=txt->mirrory()->scale(1.0,0.3)->color(64,64,64); + + o=clone((program)"/precompiled/image", + txt->xsize(),txt->ysize(), 0,0,100) + ->tuned_box(0,0,txt->xsize(),txt->ysize(), + ({({0,0,0}),({0,0,0}), + ({0,0,255}),({128,128,0})})); + + o->setcolor(255,255,255,200); + for (i=0; i<30; i++) + if (random(2)) + o->line(random(o->xsize()),o->ysize()-10-random(20+i*3), + o->xsize()-1-random(30),o->ysize()-1); + else + o->line(random(o->xsize()),o->ysize()-10-random(20+i), + random(30),o->ysize()-1); + + for (i=0; i<10; i++) + o->box(random(o->xsize()),random(o->ysize()), + random(o->xsize()),random(o->ysize()), + random(256),random(256),random(256),220); + + o -> paste_mask(txt->clear(0,255,0), + shad,0,(int)(font->baseline()*0.7)+shad->ysize()-10) + -> paste_mask(txt->clear(255,255,0), + txt->apply_matrix(({({1,2,1}),({2,4,2}),({1,2,1})})) + ->apply_matrix(({({1,2,1}),({2,4,2}),({1,2,1})})) + ->modify_by_intensity(1,0,0, 0,255,255,255,255,255)) + -> paste_mask(txt->clone() + ->tuned_box(0,0,txt->xsize()-1,txt->ysize()-1, + ({({128,128,128}),({64,128,0}), + ({64,128,0}),({255,255,0})})), + txt); + write(o->togif_fs()); + return 0; +} +</pre> + +<h2>Undocumented, yet:</h2> +<pre> +object image->select_from(int x,int y); +object image->distancesq(int r,int g,int b); +array(int) image->getpixel(int x,int y); +object image->skewx(int diff,rgb); +object image->skewy(int diff,rgb); +object image->skewx_expand(int diff,rgb); +object image->skewy_expand(int diff,rgb); +object image->rotate(int|float angle,rgb); +object image->rotate_expand(int|float angle,rgb); +object image->turbulence(colorrange,int octaves=3,float scale=1, + float xdiff=0,float ydiff=0,float cscale=1); +object image->noise(colorrange,float scale=0.1, + float xdiff=0,float ydiff=0,float cscale=1); + where colorrange is ({ float position=0..1, ({r,g,b}), + float position=0..1, ({r,g,b}), ... }) +</pre> \ No newline at end of file diff --git a/src/modules/Image/font.c b/src/modules/Image/font.c new file mode 100644 index 0000000000000000000000000000000000000000..8e3aede357ce80881dcb97bac9f16340232db885 --- /dev/null +++ b/src/modules/Image/font.c @@ -0,0 +1,572 @@ +#include <config.h> + +/* $Id: font.c,v 1.1 1997/02/11 08:35:43 hubbe Exp $ */ + +#include "global.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef HAVE_SYS_FCNTL_H +#include <sys/fcntl.h> +#endif + +#include <netinet/in.h> +#include <errno.h> + +#include "config.h" + +#include "stralloc.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "threads.h" + +#include "image.h" +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif + + +static struct program *font_program; +extern struct program *image_program; + +#define THIS (*(struct font **)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +struct font +{ + unsigned long height; /* height of character rectangles */ + unsigned long baseline; /* baseline of characters */ +#ifdef HAVE_MMAP + unsigned long mmaped_size; /* if 0 - not mmaped: just free() mem */ +#endif + void *mem; /* pointer to mmaped/malloced memory */ + unsigned long chars; /* number of characters */ + float xspacing_scale; /* Fraction of spacing to use */ + float yspacing_scale; /* Fraction of spacing to use */ + enum { + J_LEFT, + J_RIGHT, + J_CENTER + } justification; + struct _char + { + unsigned long width; /* character rectangle has this width in pixels */ + unsigned long spacing; /* pixels to next character */ + unsigned char *pixels; /* character rectangle */ + } charinfo [1]; /* many!! */ +}; + +/***************** init & exit *********************************/ + +static inline void free_font_struct(struct font *font) +{ + if (font) + { + if (font->mem) + { +#ifdef HAVE_MMAP + munmap(font->mem,font->mmaped_size); +#else + free(font->mem); +#endif + } + free(font); + } +} + +static void init_font_struct(struct object *o) +{ + THIS=NULL; +} + +static void exit_font_struct(struct object *obj) +{ + free_font_struct(THIS); + THIS=NULL; +} + +/***************** internals ***********************************/ + + +static inline int my_read(int from, char *buf, int towrite) +{ + int res; + while((res = read(from, buf, towrite)) < 0) + { + switch(errno) + { + case EAGAIN: case EINTR: + continue; + + default: + res = 0; + return 0; + } + } + return res; +} + +static inline long file_size(int fd) +{ + struct stat tmp; + int res; + if(!fstat(fd, &tmp)) return res = tmp.st_size; + return -1; +} + +static inline write_char(struct _char *ci, + rgb_group *pos, + INT32 xsize, + INT32 height) +{ + rgb_group *nl; + INT32 x,y; + unsigned char *p; + + p=ci->pixels; + + for (y=height; y>0; y--) + { + nl=pos+xsize; + for (x=(INT32)ci->width; x>0; x--) + { + register char c; + c=255-*p; + if (pos->r==0) + pos->r=pos->g=pos->b=c; + else if (pos->r+c>255) + pos->r=pos->g=pos->b=255; + else + { + pos->r+=c; + pos->g+=c; + pos->b+=c; + } + pos++; + p++; + } + pos=nl; + } +} + +/***************** methods *************************************/ + + +void font_load(INT32 args) +{ + int fd; + + if (args<1 + || sp[-args].type!=T_STRING) + error("font->read: illegal or wrong number of arguments\n"); + + if (THIS) + { + free_font_struct(THIS); + THIS=NULL; + } + do + { +#ifdef FONT_DEBUG + fprintf(stderr,"FONT open '%s'\n",sp[-args].u.string->str); +#endif + fd = open(sp[-args].u.string->str,O_RDONLY); + } while(fd < 0 && errno == EINTR); + + if (fd >= 0) + { + long size; + struct font *new; + + size = file_size(fd); + if (size > 0) + { + new=THIS=(struct font *)xalloc(sizeof(struct font)); + + THREADS_ALLOW(); +#ifdef HAVE_MMAP + new->mem = + mmap(0,size,PROT_READ,MAP_SHARED,fd,0); + new->mmaped_size=size; +#else + new->mem = malloc(size); + if ((new->mem) && (!my_read(fd,new->mem,size))) { + free(new->mem); + new->mem = NULL; + } +#endif + THREADS_DISALLOW(); + + if (THIS->mem) + { + int i; + + struct file_head + { + unsigned INT32 cookie; + unsigned INT32 version; + unsigned INT32 chars; + unsigned INT32 height; + unsigned INT32 baseline; + unsigned INT32 o[1]; + } *fh; + struct char_head + { + unsigned INT32 width; + unsigned INT32 spacing; + unsigned char data[1]; + } *ch; + +#ifdef FONT_DEBUG + fprintf(stderr,"FONT mapped ok\n"); +#endif + + fh=(struct file_head*)THIS->mem; + + if (ntohl(fh->cookie)==0x464f4e54) /* "FONT" */ + { +#ifdef FONT_DEBUG + fprintf(stderr,"FONT cookie ok\n"); +#endif + if (ntohl(fh->version)==1) + { + unsigned long i; + +#ifdef FONT_DEBUG + fprintf(stderr,"FONT version 1\n"); +#endif + + THIS->chars=ntohl(fh->chars); + + new=malloc(sizeof(struct font)+ + sizeof(struct _char)*(THIS->chars-1)); + new->mem=THIS->mem; +#ifdef HAVE_MMAP + new->mmaped_size=THIS->mmaped_size; +#endif + new->chars=THIS->chars; + new->xspacing_scale = 1.0; + new->yspacing_scale = 1.0; + new->justification = J_LEFT; + free(THIS); + THIS=new; + + THIS->height=ntohl(fh->height); + THIS->baseline=ntohl(fh->baseline); + + for (i=0; i<THIS->chars; i++) + { + if (i*sizeof(INT32)<(unsigned long)size + && ntohl(fh->o[i])<(unsigned long)size + && ! ( ntohl(fh->o[i]) % 4) ) /* must be aligned */ + { + ch=(struct char_head*) + ((char *)(THIS->mem)+ntohl(fh->o[i])); + THIS->charinfo[i].width = ntohl(ch->width); + THIS->charinfo[i].spacing = ntohl(ch->spacing); + THIS->charinfo[i].pixels = ch->data; + } + else /* illegal <tm> offset or illegal align */ + { +#ifdef FONT_DEBUG + fprintf(stderr,"FONT failed on char %02xh %d '%c'\n", + i,i,i); +#endif + free_font_struct(new); + THIS=NULL; + pop_n_elems(args); + push_int(0); + return; + } + + } + + close(fd); + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); /* success */ +#ifdef FONT_DEBUG + fprintf(stderr,"FONT successfully loaded\n"); +#endif + return; + } /* wrong version */ +#ifdef FONT_DEBUG + else fprintf(stderr,"FONT unknown version\n"); +#endif + } /* wrong cookie */ +#ifdef FONT_DEBUG + else fprintf(stderr,"FONT wrong cookie\n"); +#endif + } /* mem failure */ +#ifdef FONT_DEBUG + else fprintf(stderr,"FONT mem failure\n"); +#endif + free_font_struct(THIS); + THIS=NULL; + } /* size failure */ +#ifdef FONT_DEBUG + else fprintf(stderr,"FONT size failure\n"); +#endif + close(fd); + } /* fd failure */ +#ifdef FONT_DEBUG + else fprintf(stderr,"FONT fd failure\n"); +#endif + + pop_n_elems(args); + push_int(0); + return; +} + +void font_write(INT32 args) +{ + struct object *o; + struct image *img; + INT32 xsize=0,i,maxwidth,c,maxwidth2,j; + int *width_of; + + if (!THIS) + error("font->write: no font loaded\n"); + + maxwidth2=0; + + width_of=(int *)malloc((args+1)*sizeof(int)); + if(!width_of) + error("Out of memory\n"); + for (j=0; j<args; j++) + { + if (sp[j-args].type!=T_STRING) + error("font->write: illegal argument(s)\n"); + + xsize = 0; + maxwidth = 0; + + for (i = 0; i < sp[j-args].u.string->len; i++) + { + c=EXTRACT_UCHAR(sp[j-args].u.string->str+i); + if (c < (INT32)THIS->chars) + { + if (xsize + (signed long)THIS->charinfo[c].width > maxwidth) + maxwidth = xsize + THIS->charinfo[c].width; + xsize+=(signed long)((float)THIS->charinfo[c].spacing + *(float)THIS->xspacing_scale); + } + } + if (xsize>maxwidth) maxwidth=xsize; + width_of[j]=maxwidth; + if (maxwidth>maxwidth2) maxwidth2=maxwidth; + } + + o = clone(image_program,0); + img = ((struct image*)o->storage); + img->xsize = maxwidth2; + if(args>1) + img->ysize = THIS->height+((double)THIS->height*(double)(args-1)*(double)THIS->yspacing_scale)+1; + else + img->ysize = THIS->height+1; + img->rgb.r=img->rgb.g=img->rgb.b=255; + img->img=malloc(img->xsize*img->ysize*sizeof(rgb_group)); + + if (!img) { free_object(o); free(width_of); error("Out of memory\n"); } + + MEMSET(img->img,0,img->xsize*img->ysize*sizeof(rgb_group)); + + for (j=0; j<args; j++) + { + switch(THIS->justification) + { + case J_LEFT: xsize = 0; break; + case J_RIGHT: xsize = img->xsize-width_of[j]-1; break; + case J_CENTER: xsize = img->xsize/2-width_of[j]/2-1; break; + } + if(xsize<0) xsize=0; + for (i = 0; i < (int)sp[j-args].u.string->len; i++) + { + c=EXTRACT_UCHAR(sp[j-args].u.string->str+i); + if ( c < (INT32)THIS->chars) + { + write_char(THIS->charinfo+c, + (img->img+xsize)+(img->xsize*(int)(j*THIS->height + *THIS->yspacing_scale)), + img->xsize, + THIS->height); + xsize += THIS->charinfo[c].spacing*THIS->xspacing_scale; + } + } + } + free(width_of); + pop_n_elems(args); + push_object(o); +} + +void font_height(INT32 args) +{ + pop_n_elems(args); + if (THIS) + push_int(THIS->height); + else + push_int(0); +} + +void font_text_extents(INT32 args) +{ + INT32 xsize,i,maxwidth,c,maxwidth2,j; + + if (!THIS) + error("font->text_extents: no font loaded\n"); + + maxwidth2=0; + + for (j=0; j<args; j++) + { + if (sp[j-args].type!=T_STRING) + error("font->text_extents: illegal argument(s)\n"); + + xsize = 0; + maxwidth = 0; + + for (i = 0; i < sp[j-args].u.string->len; i++) + { + c=EXTRACT_UCHAR(sp[j-args].u.string->str+i); + if (c < (INT32)THIS->chars) + { + if (xsize + (signed long)THIS->charinfo[c].width > maxwidth) + maxwidth = xsize + THIS->charinfo[c].width; + xsize += THIS->charinfo[c].spacing*THIS->xspacing_scale; + } + } + + if (xsize>maxwidth) maxwidth=xsize; + if (maxwidth>maxwidth2) maxwidth2=maxwidth; + } + push_int(maxwidth2); + push_int(args * THIS->height * THIS->yspacing_scale); +} + + +void font_set_xspacing_scale(INT32 args) +{ + if(!THIS) error("font->set_xspacing_scale(FLOAT): No font loaded.\n"); + if(!args) error("font->set_xspacing_scale(FLOAT): No argument!\n"); + if(sp[-args].type!=T_FLOAT) + error("font->set_xspacing_scale(FLOAT): Wrong type of argument!\n"); + + THIS->xspacing_scale = (double)sp[-args].u.float_number; +/*fprintf(stderr, "Setting xspacing to %f\n", THIS->xspacing_scale);*/ + if(THIS->xspacing_scale < 0.0) + THIS->xspacing_scale=0.1; + pop_stack(); +} + + +void font_set_yspacing_scale(INT32 args) +{ + if(!THIS) error("font->set_yspacing_scale(FLOAT): No font loaded.\n"); + if(!args) error("font->set_yspacing_scale(FLOAT): No argument!\n"); + if(sp[-args].type!=T_FLOAT) + error("font->set_yspacing_scale(FLOAT): Wrong type of argument!\n"); + + THIS->yspacing_scale = (double)sp[-args].u.float_number; +/*fprintf(stderr, "Setting yspacing to %f\n", THIS->yspacing_scale);*/ + if(THIS->yspacing_scale <= 0.0) + THIS->yspacing_scale=0.1; + pop_stack(); +} + +void font_baseline(INT32 args) +{ + pop_n_elems(args); + if (THIS) + push_int(THIS->baseline); + else + push_int(0); +} + +void font_set_center(INT32 args) +{ + pop_n_elems(args); + if(THIS) THIS->justification=J_CENTER; +} + +void font_set_right(INT32 args) +{ + pop_n_elems(args); + if(THIS) THIS->justification=J_RIGHT; +} + +void font_set_left(INT32 args) +{ + pop_n_elems(args); + if(THIS) THIS->justification=J_LEFT; +} + + +/***************** global init etc *****************************/ + +/* + +int load(string filename); // load font file, true is success +object write(string text); // new image object +int height(); // font heigth +int baseline(); // font baseline + +*/ + +void init_font_programs(void) +{ + start_new_program(); + add_storage(sizeof(struct font*)); + + add_function("load",font_load, + "function(string:int)",0); + + add_function("write",font_write, + "function(string:object)",0); + + add_function("height",font_height, + "function(:int)",0); + + add_function("baseline",font_baseline, + "function(:int)",0); + + add_function("extents",font_text_extents, + "function(string ...:array(int))",0); + + add_function("set_x_spacing",font_set_xspacing_scale, + "function(float:void)",0); + + add_function("set_y_spacing",font_set_yspacing_scale, + "function(float:void)",0); + + add_function("center", font_set_center, "function(void:void)", 0); + add_function("left", font_set_left, "function(void:void)", 0); + add_function("right", font_set_right, "function(void:void)", 0); + + + set_init_callback(init_font_struct); + set_exit_callback(exit_font_struct); + + font_program=end_program(); + add_program_constant("font",font_program, 0); +} + +void exit_font(void) +{ + if(font_program) + { + free_program(font_program); + font_program=0; + } +} + + diff --git a/src/modules/Image/image.c b/src/modules/Image/image.c new file mode 100644 index 0000000000000000000000000000000000000000..3a654b5e22c4c01a22824d9eff8863a461891331 --- /dev/null +++ b/src/modules/Image/image.c @@ -0,0 +1,1805 @@ +/* $Id: image.c,v 1.1 1997/02/11 08:35:43 hubbe Exp $ */ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +RCSID("$Id: image.c,v 1.1 1997/02/11 08:35:43 hubbe Exp $"); +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "threads.h" +#include "array.h" +#include "error.h" + +#include "image.h" +#include "builtin_functions.h" + +struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) +#define testrange(x) max(min((x),255),0) + +#define sq(x) ((x)*(x)) + +#define CIRCLE_STEPS 128 +static INT32 circle_sin_table[CIRCLE_STEPS]; +#define circle_sin(x) circle_sin_table[((x)+CIRCLE_STEPS)%CIRCLE_STEPS] +#define circle_cos(x) circle_sin((x)-CIRCLE_STEPS/4) + +#define circle_sin_mul(x,y) ((circle_sin(x)*(y))/4096) +#define circle_cos_mul(x,y) ((circle_cos(x)*(y))/4096) + + + +#if 0 +#include <sys/resource.h> +#define CHRONO(X) chrono(X) + +static void chrono(char *x) +{ + struct rusage r; + static struct rusage rold; + getrusage(RUSAGE_SELF,&r); + fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x, + r.ru_utime.tv_sec,r.ru_utime.tv_usec, + + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0) + +r.ru_utime.tv_sec-rold.ru_utime.tv_sec, + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0) + + r.ru_utime.tv_usec-rold.ru_utime.tv_usec + ); + + rold=r; +} +#else +#define CHRONO(X) +#endif + + +/***************** init & exit *********************************/ + +static int obj_counter=0; + +static void init_image_struct(struct object *obj) +{ + THIS->img=NULL; + THIS->rgb.r=0; + THIS->rgb.g=0; + THIS->rgb.b=0; +/* fprintf(stderr,"init %lx (%d)\n",obj,++obj_counter);*/ +} + +static void exit_image_struct(struct object *obj) +{ + if (THIS->img) { free(THIS->img); THIS->img=NULL; } +/* + fprintf(stderr,"exit %lx (%d) %dx%d=%.1fKb\n",obj,--obj_counter, + THIS->xsize,THIS->ysize, + (THIS->xsize*THIS->ysize*3+sizeof(struct image))/1024.0); + */ +} + +/***************** internals ***********************************/ + +#define apply_alpha(x,y,alpha) \ + ((unsigned char)((y*(255L-(alpha))+x*(alpha))/255L)) + +#define set_rgb_group_alpha(dest,src,alpha) \ + ((dest).r=apply_alpha((dest).r,(src).r,alpha), \ + (dest).g=apply_alpha((dest).g,(src).g,alpha), \ + (dest).b=apply_alpha((dest).b,(src).b,alpha)) + +#define pixel(_img,x,y) ((_img)->img[(x)+(y)*(_img)->xsize]) + +#define setpixel(x,y) \ + (THIS->alpha? \ + set_rgb_group_alpha(THIS->img[(x)+(y)*THIS->xsize],THIS->rgb,THIS->alpha): \ + ((pixel(THIS,x,y)=THIS->rgb),0)) + +#define setpixel_test(x,y) \ + (((x)<0||(y)<0||(x)>=THIS->xsize||(y)>=THIS->ysize)? \ + 0:(setpixel(x,y),0)) + +static INLINE void getrgb(struct image *img, + INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + img->rgb.r=(unsigned char)sp[-args+args_start].u.integer; + img->rgb.g=(unsigned char)sp[1-args+args_start].u.integer; + img->rgb.b=(unsigned char)sp[2-args+args_start].u.integer; + if (args-args_start>=4) + if (sp[3-args+args_start].type!=T_INT) + error("Illegal alpha argument to %s\n",name); + else + img->alpha=sp[3-args+args_start].u.integer; + else + img->alpha=0; +} + +static INLINE void getrgbl(rgbl_group *rgb,INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + rgb->r=sp[-args+args_start].u.integer; + rgb->g=sp[1-args+args_start].u.integer; + rgb->b=sp[2-args+args_start].u.integer; +} + +static INLINE void img_line(INT32 x1,INT32 y1,INT32 x2,INT32 y2) +{ + INT32 pixelstep,pos; + if (x1==x2) + { + if (y1>y2) y1^=y2,y2^=y1,y1^=y2; + if (x1<0||x1>=THIS->xsize|| + y2<0||y1>=THIS->ysize) return; + if (y1<0) y1=0; + if (y2>=THIS->ysize) y2=THIS->ysize-1; + for (;y1<=y2;y1++) setpixel_test(x1,y1); + return; + } + else if (y1==y2) + { + if (x1>x2) x1^=x2,x2^=x1,x1^=x2; + if (y1<0||y1>=THIS->ysize|| + x2<0||x1>=THIS->xsize) return; + if (x1<0) x1=0; + if (x2>=THIS->xsize) x2=THIS->xsize-1; + for (;x1<=x2;x1++) setpixel_test(x1,y1); + return; + } + else if (abs(x2-x1)<abs(y2-y1)) /* mostly vertical line */ + { + if (y1>y2) y1^=y2,y2^=y1,y1^=y2, + x1^=x2,x2^=x1,x1^=x2; + pixelstep=((x2-x1)*1024)/(y2-y1); + pos=x1*1024; + for (;y1<=y2;y1++) + { + setpixel_test((pos+512)/1024,y1); + pos+=pixelstep; + } + } + else /* mostly horisontal line */ + { + if (x1>x2) y1^=y2,y2^=y1,y1^=y2, + x1^=x2,x2^=x1,x1^=x2; + pixelstep=((y2-y1)*1024)/(x2-x1); + pos=y1*1024; + for (;x1<=x2;x1++) + { + setpixel_test(x1,(pos+512)/1024); + pos+=pixelstep; + } + } +} + +static INLINE rgb_group _pixel_apply_matrix(struct image *img, + int x,int y, + int width,int height, + rgbd_group *matrix, + rgb_group default_rgb, + double div) +{ + rgb_group res; + int i,j,bx,by,xp,yp; + int sumr,sumg,sumb,r,g,b; + float qdiv=1.0/div; + + sumr=sumg=sumb=0; + r=g=b=0; + + bx=width/2; + by=height/2; + + for (xp=x-bx,i=0; i<width; i++,xp++) + for (yp=y-by,j=0; j<height; j++,yp++) + if (xp>=0 && xp<img->xsize && yp>=0 && yp<img->ysize) + { + r+=matrix[i+j*width].r*img->img[xp+yp*img->xsize].r; + g+=matrix[i+j*width].g*img->img[xp+yp*img->xsize].g; + b+=matrix[i+j*width].b*img->img[xp+yp*img->xsize].b; +#ifdef MATRIX_DEBUG + fprintf(stderr,"%d,%d %d,%d->%d,%d,%d\n", + i,j,xp,yp, + img->img[x+i+(y+j)*img->xsize].r, + img->img[x+i+(y+j)*img->xsize].g, + img->img[x+i+(y+j)*img->xsize].b); +#endif + sumr+=matrix[i+j*width].r; + sumg+=matrix[i+j*width].g; + sumb+=matrix[i+j*width].b; + } + if (sumr) res.r=testrange(default_rgb.r+r/(sumr*div)); + else res.r=testrange(r*qdiv+default_rgb.r); + if (sumg) res.g=testrange(default_rgb.g+g/(sumg*div)); + else res.g=testrange(g*qdiv+default_rgb.g); + if (sumb) res.b=testrange(default_rgb.g+b/(sumb*div)); + else res.b=testrange(b*qdiv+default_rgb.b); +#ifdef MATRIX_DEBUG + fprintf(stderr,"->%d,%d,%d\n",res.r,res.g,res.b); +#endif + return res; +} + + +void img_apply_matrix(struct image *dest, + struct image *img, + int width,int height, + rgbd_group *matrix, + rgb_group default_rgb, + double div) +{ + rgb_group *d,*ip,*dp; + rgbd_group *mp; + int i,j,x,y,bx,by,ex,ey,xp,yp; + int sumr,sumg,sumb; + double qr,qg,qb; + register double r=0,g=0,b=0; + +THREADS_ALLOW(); + + sumr=sumg=sumb=0; + for (i=0; i<width; i++) + for (j=0; j<height; j++) + { + sumr+=matrix[i+j*width].r; + sumg+=matrix[i+j*width].g; + sumb+=matrix[i+j*width].b; + } + + if (!sumr) sumr=1; sumr*=div; qr=1.0/sumr; + if (!sumg) sumg=1; sumg*=div; qg=1.0/sumg; + if (!sumb) sumb=1; sumb*=div; qb=1.0/sumb; + + bx=width/2; + by=height/2; + ex=width-bx; + ey=height-by; + + d=malloc(sizeof(rgb_group)*img->xsize*img->ysize +1); + + if(!d) error("Out of memory.\n"); + +CHRONO("apply_matrix, one"); + + for (y=by; y<img->ysize-ey; y++) + { + dp=d+y*img->xsize+by; + for (x=bx; x<img->xsize-ex; x++) + { + r=g=b=0; + mp=matrix; + for (yp=y-by,j=0; j<height; j++,yp++) + { + ip=img->img+(x-bx)+yp*img->xsize; + for (i=0; i<width; i++) + { + r+=ip->r*mp->r; + g+=ip->g*mp->g; + b+=ip->b*mp->b; +#ifdef MATRIX_DEBUG + fprintf(stderr,"%d,%d ->%d,%d,%d\n", + i,j, + img->img[x+i+(y+j)*img->xsize].r, + img->img[x+i+(y+j)*img->xsize].g, + img->img[x+i+(y+j)*img->xsize].b); +#endif + mp++; + ip++; + } + } +#ifdef MATRIX_DEBUG + fprintf(stderr,"->%d,%d,%d\n",r/sumr,g/sumg,b/sumb); +#endif + r=default_rgb.r+(int)(r*qr+0.5); dp->r=testrange(r); + g=default_rgb.g+(int)(g*qg+0.5); dp->g=testrange(g); + b=default_rgb.b+(int)(b*qb+0.5); dp->b=testrange(b); + dp++; + } + } + +CHRONO("apply_matrix, two"); + + for (y=0; y<img->ysize; y++) + { + for (x=0; x<bx; x++) + d[x+y*img->xsize]=_pixel_apply_matrix(img,x,y,width,height, + matrix,default_rgb,div); + for (x=img->xsize-ex; x<img->xsize; x++) + d[x+y*img->xsize]=_pixel_apply_matrix(img,x,y,width,height, + matrix,default_rgb,div); + } + + for (x=0; x<img->xsize; x++) + { + for (y=0; y<by; y++) + d[x+y*img->xsize]=_pixel_apply_matrix(img,x,y,width,height, + matrix,default_rgb,div); + for (y=img->ysize-ey; y<img->ysize; y++) + d[x+y*img->xsize]=_pixel_apply_matrix(img,x,y,width,height, + matrix,default_rgb,div); + } + +CHRONO("apply_matrix, three"); + + if (dest->img) free(dest->img); + *dest=*img; + dest->img=d; + +THREADS_DISALLOW(); +} + +/***************** methods *************************************/ + +void image_create(INT32 args) +{ + if (args<2) return; + if (sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT) + error("Illegal arguments to image->clone()\n"); + + getrgb(THIS,2,args,"image->create()"); + + if (THIS->img) free(THIS->img); + + THIS->xsize=sp[-args].u.integer; + THIS->ysize=sp[1-args].u.integer; + if (THIS->xsize<0) THIS->xsize=0; + if (THIS->ysize<0) THIS->ysize=0; + + THIS->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize +1); + if (!THIS->img) + error("out of memory\n"); + + + img_clear(THIS->img,THIS->rgb,THIS->xsize*THIS->ysize); + pop_n_elems(args); +} + +void image_clone(INT32 args) +{ + struct object *o; + struct image *img; + + if (args) + if (args<2|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT) + error("Illegal arguments to image->clone()\n"); + + o=clone(image_program,0); + img=(struct image*)(o->storage); + *img=*THIS; + + if (args) + { + if(sp[-args].u.integer < 0 || + sp[1-args].u.integer < 0) + error("Illegal size to image->clone()\n"); + img->xsize=sp[-args].u.integer; + img->ysize=sp[1-args].u.integer; + } + + getrgb(img,2,args,"image->clone()"); + + if (img->xsize<0) img->xsize=1; + if (img->ysize<0) img->ysize=1; + + img->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize +1); + if (THIS->img) + { + if (!img->img) + { + free_object(o); + error("out of memory\n"); + } + if (img->xsize==THIS->xsize + && img->ysize==THIS->ysize) + MEMCPY(img->img,THIS->img,sizeof(rgb_group)*img->xsize*img->ysize); + else + img_crop(img,THIS, + 0,0,img->xsize-1,img->ysize-1); + + } + else + img_clear(img->img,img->rgb,img->xsize*img->ysize); + + pop_n_elems(args); + push_object(o); +} + +void image_clear(INT32 args) +{ + struct object *o; + struct image *img; + + o=clone(image_program,0); + img=(struct image*)(o->storage); + *img=*THIS; + + getrgb(img,0,args,"image->clear()"); + + img->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize +1); + if (!img->img) + { + free_object(o); + error("out of memory\n"); + } + + img_clear(img->img,img->rgb,img->xsize*img->ysize); + + pop_n_elems(args); + push_object(o); +} + +void image_copy(INT32 args) +{ + struct object *o; + struct image *img; + + if (!args) + { + o=clone(image_program,0); + if (THIS->img) img_clone((struct image*)o->storage,THIS); + pop_n_elems(args); + push_object(o); + return; + } + if (args<4|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT|| + sp[2-args].type!=T_INT|| + sp[3-args].type!=T_INT) + error("illegal arguments to image->copy()\n"); + + if (!THIS->img) error("no image\n"); + + getrgb(THIS,2,args,"image->crop()"); + + o=clone(image_program,0); + img=(struct image*)(o->storage); + + img_crop(img,THIS, + sp[-args].u.integer,sp[1-args].u.integer, + sp[2-args].u.integer,sp[3-args].u.integer); + + pop_n_elems(args); + push_object(o); +} + +static void image_change_color(INT32 args) + +{ + /* ->change_color([int from-r,g,b,] int to-r,g,b); */ + rgb_group from,to,*s,*d; + INT32 left; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + if (args<3) error("too few arguments to image->change_color()\n"); + + if (args<6) + { + to=THIS->rgb; + getrgb(THIS,0,args,"image->change_color()"); + from=THIS->rgb; + } + else + { + getrgb(THIS,0,args,"image->change_color()"); + from=THIS->rgb; + getrgb(THIS,3,args,"image->change_color()"); + to=THIS->rgb; + } + + o=clone(image_program,0); + img=(struct image*)(o->storage); + *img=*THIS; + + if (!(img->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize +1))) + { + free_object(o); + error("out of memory\n"); + } + + left=THIS->xsize*THIS->ysize; + s=THIS->img; + d=img->img; + while (left--) + { + if (s->r==from.r && s->g==from.g && s->b==from.b) + *d=to; + else + *d=*s; + d++; s++; + } + + pop_n_elems(args); + push_object(o); +} + +static INLINE int try_autocrop_vertical(INT32 x,INT32 y,INT32 y2, + INT32 rgb_set,rgb_group *rgb) +{ + if (!rgb_set) *rgb=pixel(THIS,x,y); + for (;y<=y2; y++) + if (pixel(THIS,x,y).r!=rgb->r || + pixel(THIS,x,y).g!=rgb->g || + pixel(THIS,x,y).b!=rgb->b) return 0; + return 1; +} + +static INLINE int try_autocrop_horisontal(INT32 y,INT32 x,INT32 x2, + INT32 rgb_set,rgb_group *rgb) +{ + if (!rgb_set) *rgb=pixel(THIS,x,y); + for (;x<=x2; x++) + if (pixel(THIS,x,y).r!=rgb->r || + pixel(THIS,x,y).g!=rgb->g || + pixel(THIS,x,y).b!=rgb->b) return 0; + return 1; +} + +void image_autocrop(INT32 args) +{ + INT32 border=0,x1,y1,x2,y2; + rgb_group rgb; + int rgb_set=0,done; + struct object *o; + struct image *img; + int left=1,right=1,top=1,bottom=1; + + if (args) + if (sp[-args].type!=T_INT) + error("Illegal argument to image->autocrop()\n"); + else + border=sp[-args].u.integer; + + if (args>=5) + { + left=!(sp[1-args].type==T_INT && sp[1-args].u.integer==0); + right=!(sp[2-args].type==T_INT && sp[2-args].u.integer==0); + top=!(sp[3-args].type==T_INT && sp[3-args].u.integer==0); + bottom=!(sp[4-args].type==T_INT && sp[4-args].u.integer==0); + getrgb(THIS,5,args,"image->autocrop()"); + } + else getrgb(THIS,1,args,"image->autocrop()"); + + if (!THIS->img) + { + error("no image\n"); + return; + } + + x1=y1=0; + x2=THIS->xsize-1; + y2=THIS->ysize-1; + + while (x2>x1 && y2>y1) + { + done=0; + if (left && + try_autocrop_vertical(x1,y1,y2,rgb_set,&rgb)) x1++,done=rgb_set=1; + if (right && + x2>x1 && + try_autocrop_vertical(x2,y1,y2,rgb_set,&rgb)) x2--,done=rgb_set=1; + if (top && + try_autocrop_horisontal(y1,x1,x2,rgb_set,&rgb)) y1++,done=rgb_set=1; + if (bottom && + y2>y1 && + try_autocrop_horisontal(y2,x1,x2,rgb_set,&rgb)) y2--,done=rgb_set=1; + if (!done) break; + } + + o=clone(image_program,0); + img=(struct image*)(o->storage); + + img_crop(img,THIS,x1-border,y1-border,x2+border,y2+border); + + pop_n_elems(args); + push_object(o); +} + + +void image_setcolor(INT32 args) +{ + if (args<3) + error("illegal arguments to image->setcolor()\n"); + getrgb(THIS,0,args,"image->setcolor()"); + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_setpixel(INT32 args) +{ + INT32 x,y; + if (args<2|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT) + error("Illegal arguments to image->setpixel()\n"); + getrgb(THIS,2,args,"image->setpixel()"); + if (!THIS->img) return; + x=sp[-args].u.integer; + y=sp[1-args].u.integer; + if (!THIS->img) return; + setpixel_test(x,y); + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_getpixel(INT32 args) +{ + INT32 x,y; + rgb_group rgb; + + if (args<2|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT) + error("Illegal arguments to image->getpixel()\n"); + + if (!THIS->img) error("No image.\n"); + + x=sp[-args].u.integer; + y=sp[1-args].u.integer; + if (!THIS->img) return; + if(x<0||y<0||x>=THIS->xsize||y>=THIS->ysize) + rgb=THIS->rgb; + else + rgb=pixel(THIS,x,y); + + pop_n_elems(args); + push_int(rgb.r); + push_int(rgb.g); + push_int(rgb.b); + f_aggregate(3); +} + +void image_line(INT32 args) +{ + if (args<4|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT|| + sp[2-args].type!=T_INT|| + sp[3-args].type!=T_INT) + error("Illegal arguments to image->line()\n"); + getrgb(THIS,4,args,"image->line()"); + if (!THIS->img) return; + + img_line(sp[-args].u.integer, + sp[1-args].u.integer, + sp[2-args].u.integer, + sp[3-args].u.integer); + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_box(INT32 args) +{ + if (args<4|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT|| + sp[2-args].type!=T_INT|| + sp[3-args].type!=T_INT) + error("Illegal arguments to image->box()\n"); + getrgb(THIS,4,args,"image->box()"); + if (!THIS->img) return; + + img_box(sp[-args].u.integer, + sp[1-args].u.integer, + sp[2-args].u.integer, + sp[3-args].u.integer); + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_circle(INT32 args) +{ + INT32 x,y,rx,ry; + INT32 i; + + if (args<4|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT|| + sp[2-args].type!=T_INT|| + sp[3-args].type!=T_INT) + error("illegal arguments to image->circle()\n"); + getrgb(THIS,4,args,"image->circle()"); + if (!THIS->img) return; + + x=sp[-args].u.integer; + y=sp[1-args].u.integer; + rx=sp[2-args].u.integer; + ry=sp[3-args].u.integer; + + for (i=0; i<CIRCLE_STEPS; i++) + img_line(x+circle_sin_mul(i,rx), + y+circle_cos_mul(i,ry), + x+circle_sin_mul(i+1,rx), + y+circle_cos_mul(i+1,ry)); + THISOBJ->refs++; + push_object(THISOBJ); +} + +static INLINE void get_rgba_group_from_array_index(rgba_group *rgba,struct array *v,INT32 index) +{ + struct svalue s,s2; + array_index_no_free(&s,v,index); + if (s.type!=T_ARRAY|| + s.u.array->size<3) + rgba->r=rgba->b=rgba->g=rgba->alpha=0; + else + { + array_index_no_free(&s2,s.u.array,0); + if (s2.type!=T_INT) rgba->r=0; else rgba->r=s2.u.integer; + array_index(&s2,s.u.array,1); + if (s2.type!=T_INT) rgba->g=0; else rgba->g=s2.u.integer; + array_index(&s2,s.u.array,2); + if (s2.type!=T_INT) rgba->b=0; else rgba->b=s2.u.integer; + if (s.u.array->size>=4) + { + array_index(&s2,s.u.array,3); + if (s2.type!=T_INT) rgba->alpha=0; else rgba->alpha=s2.u.integer; + } + else rgba->alpha=0; + free_svalue(&s2); + } + free_svalue(&s); + return; +} + +static INLINE void + add_to_rgba_sum_with_factor(rgba_group *sum, + rgba_group rgba, + float factor) +{ + sum->r=testrange(sum->r+(INT32)(rgba.r*factor+0.5)); + sum->g=testrange(sum->g+(INT32)(rgba.g*factor+0.5)); + sum->b=testrange(sum->b+(INT32)(rgba.b*factor+0.5)); + sum->alpha=testrange(sum->alpha+(INT32)(rgba.alpha*factor+0.5)); +} + +static INLINE void + add_to_rgb_sum_with_factor(rgb_group *sum, + rgba_group rgba, + float factor) +{ + sum->r=testrange(sum->r+(INT32)(rgba.r*factor+0.5)); + sum->g=testrange(sum->g+(INT32)(rgba.g*factor+0.5)); + sum->b=testrange(sum->b+(INT32)(rgba.b*factor+0.5)); +} + +void image_tuned_box(INT32 args) +{ + INT32 x1,y1,x2,y2,xw,yw,x,y; + rgba_group topleft,topright,bottomleft,bottomright,sum,sumzero={0,0,0,0}; + rgb_group *img; + struct image *this; + + if (args<5|| + sp[-args].type!=T_INT|| + sp[1-args].type!=T_INT|| + sp[2-args].type!=T_INT|| + sp[3-args].type!=T_INT|| + sp[4-args].type!=T_ARRAY|| + sp[4-args].u.array->size<4) + error("Illegal number of arguments to image->tuned_box()\n"); + + if (!THIS->img) + error("no image\n"); + + x1=sp[-args].u.integer; + y1=sp[1-args].u.integer; + x2=sp[2-args].u.integer; + y2=sp[3-args].u.integer; + + get_rgba_group_from_array_index(&topleft,sp[4-args].u.array,0); + get_rgba_group_from_array_index(&topright,sp[4-args].u.array,1); + get_rgba_group_from_array_index(&bottomleft,sp[4-args].u.array,2); + get_rgba_group_from_array_index(&bottomright,sp[4-args].u.array,3); + + if (x1>x2) x1^=x2,x2^=x1,x1^=x2, + sum=topleft,topleft=topright,topright=sum, + sum=bottomleft,bottomleft=bottomright,bottomright=sum; + if (y1>y2) y1^=y2,y2^=y1,y1^=y2, + sum=topleft,topleft=bottomleft,bottomleft=sum, + sum=topright,topright=bottomright,bottomright=sum; + if (x2<0||y2<0||x1>=THIS->xsize||y1>=THIS->ysize) return; + + xw=x2-x1; + yw=y2-y1; + + this=THIS; + THREADS_ALLOW(); + + for (x=max(0,-x1); x<=xw && x+x1<THIS->xsize; x++) + { +#define tune_factor(a,aw) (1.0-((float)(a)/(aw))) + INT32 ymax; + float tfx1=tune_factor(x,xw); + float tfx2=tune_factor(xw-x,xw); + + ymax=min(yw,this->ysize-y1); + img=this->img+x+x1+this->xsize*max(0,y1); + if (topleft.alpha||topright.alpha||bottomleft.alpha||bottomright.alpha) + for (y=max(0,-y1); y<ymax; y++) + { + float tfy; + sum=sumzero; + + add_to_rgba_sum_with_factor(&sum,topleft,(tfy=tune_factor(y,yw))*tfx1); + add_to_rgba_sum_with_factor(&sum,topright,tfy*tfx2); + add_to_rgba_sum_with_factor(&sum,bottomleft,(tfy=tune_factor(yw-y,yw))*tfx1); + add_to_rgba_sum_with_factor(&sum,bottomright,tfy*tfx2); + + set_rgb_group_alpha(*img, sum,sum.alpha); + img+=this->xsize; + } + else + for (y=max(0,-y1); y<ymax; y++) + { + float tfy; + rgb_group sum={0,0,0}; + + add_to_rgb_sum_with_factor(&sum,topleft,(tfy=tune_factor(y,yw))*tfx1); + add_to_rgb_sum_with_factor(&sum,topright,tfy*tfx2); + add_to_rgb_sum_with_factor(&sum,bottomleft,(tfy=tune_factor(yw-y,yw))*tfx1); + add_to_rgb_sum_with_factor(&sum,bottomright,tfy*tfx2); + + *img=sum; + img+=this->xsize; + } + + } + THREADS_DISALLOW(); + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +void image_xsize(INT32 args) +{ + pop_n_elems(args); + if (THIS->img) push_int(THIS->xsize); else push_int(0); +} + +void image_ysize(INT32 args) +{ + pop_n_elems(args); + if (THIS->img) push_int(THIS->ysize); else push_int(0); +} + +void image_gray(INT32 args) +{ + INT32 x,y,div; + rgbl_group rgb; + rgb_group *d,*s; + struct object *o; + struct image *img; + + if (args<3) + { + rgb.r=87; + rgb.g=127; + rgb.b=41; + } + else + getrgbl(&rgb,0,args,"image->gray()"); + div=rgb.r+rgb.g+rgb.b; + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + x=THIS->xsize*THIS->ysize; + THREADS_ALLOW(); + while (x--) + { + d->r=d->g=d->b= + testrange( ((((long)s->r)*rgb.r+ + ((long)s->g)*rgb.g+ + ((long)s->b)*rgb.b)/div) ); + d++; + s++; + } + THREADS_DISALLOW(); + pop_n_elems(args); + push_object(o); +} + +void image_color(INT32 args) +{ + INT32 x,y; + rgbl_group rgb; + rgb_group *s,*d; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + if (args<3) + { + if (args>0 && sp[-args].type==T_INT) + rgb.r=rgb.b=rgb.g=sp[-args].u.integer; + else + rgb.r=THIS->rgb.r, + rgb.g=THIS->rgb.g, + rgb.b=THIS->rgb.b; + } + else + getrgbl(&rgb,0,args,"image->color()"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + + x=THIS->xsize*THIS->ysize; + + THREADS_ALLOW(); + while (x--) + { + d->r=testrange( (((long)rgb.r*s->r)/255) ); + d->g=testrange( (((long)rgb.g*s->g)/255) ); + d->b=testrange( (((long)rgb.b*s->b)/255) ); + d++; + s++; + } + THREADS_DISALLOW(); + + pop_n_elems(args); + push_object(o); +} + +void image_invert(INT32 args) +{ + INT32 x,y; + rgb_group *s,*d; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + + x=THIS->xsize*THIS->ysize; + THREADS_ALLOW(); + while (x--) + { + d->r=testrange( 255-s->r ); + d->g=testrange( 255-s->g ); + d->b=testrange( 255-s->b ); + d++; + s++; + } + THREADS_DISALLOW(); + + pop_n_elems(args); + push_object(o); +} + +void image_threshold(INT32 args) +{ + INT32 x,y; + rgb_group *s,*d,rgb; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + + getrgb(THIS,0,args,"image->threshold()"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + rgb=THIS->rgb; + + x=THIS->xsize*THIS->ysize; + THREADS_ALLOW(); + while (x--) + { + if (s->r>=rgb.r && + s->g>=rgb.g && + s->b>=rgb.b) + d->r=d->g=d->b=255; + else + d->r=d->g=d->b=0; + + d++; + s++; + } + THREADS_DISALLOW(); + + pop_n_elems(args); + push_object(o); +} + +void image_distancesq(INT32 args) +{ + INT32 i; + rgb_group *s,*d,rgb; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + + getrgb(THIS,0,args,"image->threshold()"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + rgb=THIS->rgb; + + THREADS_ALLOW(); + i=img->xsize*img->ysize; + while (i--) + { +#define DISTANCE(A,B) \ + (sq((long)(A).r-(B).r)+sq((long)(A).g-(B).g)+sq((long)(A).b-(B).b)) + d->r=d->g=d->b=testrange(DISTANCE(*s,rgb)>>8); + d++; s++; + } + THREADS_DISALLOW(); + + pop_n_elems(args); + push_object(o); +} + +/*#define DEBUG_ISF*/ +#define ISF_LEFT 4 +#define ISF_RIGHT 8 + +void isf_seek(int mode,int ydir,INT32 low_limit,INT32 x1,INT32 x2,INT32 y, + rgb_group *src,rgb_group *dest,INT32 xsize,INT32 ysize, + rgb_group rgb,int reclvl) +{ + INT32 x,xr; + INT32 j; + +#ifdef DEBUG_ISF + fprintf(stderr,"isf_seek reclvl=%d mode=%d, ydir=%d, low_limit=%d, x1=%d, x2=%d, y=%d, src=%lx, dest=%lx, xsize=%d, ysize=%d, rgb=%d,%d,%d\n",reclvl, + mode,ydir,low_limit,x1,x2,y,src,dest,xsize,ysize,rgb.r,rgb.g,rgb.b); +#endif + +#define MARK_DISTANCE(_dest,_value) \ + ((_dest).r=(_dest).g=(_dest).b=(max(1,255-(_value>>8)))) + if ( mode&ISF_LEFT ) /* scan left from x1 */ + { + x=x1; + while (x>0) + { + x--; +#ifdef DEBUG_ISF +fprintf(stderr,"<-- %d (",DISTANCE(rgb,src[x+y*xsize])); +fprintf(stderr," %d,%d,%d)\n",src[x+y*xsize].r,src[x+y*xsize].g,src[x+y*xsize].b); +#endif + if ( (j=DISTANCE(rgb,src[x+y*xsize])) >low_limit) + { + x++; + break; + } + else + { + if (dest[x+y*xsize].r) { x++; break; } /* been there */ + MARK_DISTANCE(dest[x+y*xsize],j); + } + } + if (x1>x) + { + isf_seek(ISF_LEFT,-ydir,low_limit, + x,x1-1,y,src,dest,xsize,ysize,rgb,reclvl+1); + } + x1=x; + } + if ( mode&ISF_RIGHT ) /* scan right from x2 */ + { + x=x2; + while (x<xsize-1) + { + x++; +#ifdef DEBUG_ISF +fprintf(stderr,"--> %d (",DISTANCE(rgb,src[x+y*xsize])); +fprintf(stderr," %d,%d,%d)\n",src[x+y*xsize].r,src[x+y*xsize].g,src[x+y*xsize].b); +#endif + if ( (j=DISTANCE(rgb,src[x+y*xsize])) >low_limit) + { + x--; + break; + } + else + { + if (dest[x+y*xsize].r) { x--; break; } /* done that */ + MARK_DISTANCE(dest[x+y*xsize],j); + } + } + if (x2<x) + { + isf_seek(ISF_RIGHT,-ydir,low_limit, + x2+1,x,y,src,dest,xsize,ysize,rgb,reclvl+1); + } + x2=x; + } + xr=x=x1; + y+=ydir; + if (y<0 || y>=ysize) return; + while (x<=x2) + { +#ifdef DEBUG_ISF +fprintf(stderr,"==> %d (",DISTANCE(rgb,src[x+y*xsize])); +fprintf(stderr," %d,%d,%d)\n",src[x+y*xsize].r,src[x+y*xsize].g,src[x+y*xsize].b); +#endif + if ( dest[x+y*xsize].r || /* seen that */ + (j=DISTANCE(rgb,src[x+y*xsize])) >low_limit) + { + if (xr<x) + isf_seek(ISF_LEFT*(xr==x1),ydir,low_limit, + xr,x-1,y,src,dest,xsize,ysize,rgb,reclvl+1); + while (++x<=x2) + if ( (j=DISTANCE(rgb,src[x+y*xsize])) <=low_limit) break; + xr=x; +/* x++; hokuspokus /law */ +/* n�n dag ska jag f�rs�ka begripa varf�r... */ + if (x>x2) return; + continue; + } + MARK_DISTANCE(dest[x+y*xsize],j); + x++; + } + if (x>xr) + isf_seek((ISF_LEFT*(xr==x1))|ISF_RIGHT,ydir,low_limit, + xr,x-1,y,src,dest,xsize,ysize,rgb,reclvl+1); +} + +void image_select_from(INT32 args) +{ + struct object *o; + struct image *img; + INT32 low_limit=0; + + if (!THIS->img) error("no image\n"); + + if (args<2 + || sp[-args].type!=T_INT + || sp[1-args].type!=T_INT) + error("Illegal argument(s) to image->select_from()\n"); + + if (args>=3) + if (sp[2-args].type!=T_INT) + error("Illegal argument 3 (edge type) to image->select_from()\n"); + else + low_limit=max(0,sp[2-args].u.integer); + else + low_limit=30; + low_limit=low_limit*low_limit; + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + MEMSET(img->img,0,sizeof(rgb_group)*img->xsize*img->ysize); + + if (sp[-args].u.integer>=0 && sp[-args].u.integer<img->xsize + && sp[1-args].u.integer>=0 && sp[1-args].u.integer<img->ysize) + { + isf_seek(ISF_LEFT|ISF_RIGHT,1,low_limit, + sp[-args].u.integer,sp[-args].u.integer, + sp[1-args].u.integer, + THIS->img,img->img,img->xsize,img->ysize, + pixel(THIS,sp[-args].u.integer,sp[1-args].u.integer),0); + isf_seek(ISF_LEFT|ISF_RIGHT,-1,low_limit, + sp[-args].u.integer,sp[-args].u.integer, + sp[1-args].u.integer, + THIS->img,img->img,img->xsize,img->ysize, + pixel(THIS,sp[-args].u.integer,sp[1-args].u.integer),0); + } + + pop_n_elems(args); + push_object(o); +} + +void image_apply_matrix(INT32 args) +{ + int width,height,i,j; + rgbd_group *matrix; + rgb_group default_rgb; + struct object *o; + double div; + +CHRONO("apply_matrix"); + + if (args<1 || + sp[-args].type!=T_ARRAY) + error("Illegal arguments to image->apply_matrix()\n"); + + if (args>3) + if (sp[1-args].type!=T_INT || + sp[2-args].type!=T_INT || + sp[3-args].type!=T_INT) + error("Illegal argument(s) (2,3,4) to image->apply_matrix()\n"); + else + { + default_rgb.r=sp[1-args].u.integer; + default_rgb.g=sp[1-args].u.integer; + default_rgb.b=sp[1-args].u.integer; + } + else + { + default_rgb.r=0; + default_rgb.g=0; + default_rgb.b=0; + } + + if (args>4 + && sp[4-args].type==T_INT) + { + div=sp[4-args].u.integer; + if (!div) div=1; + } + else if (args>4 + && sp[4-args].type==T_FLOAT) + { + div=sp[4-args].u.float_number; + if (!div) div=1; + } + else div=1; + + height=sp[-args].u.array->size; + width=-1; + for (i=0; i<height; i++) + { + struct svalue s; + array_index_no_free(&s,sp[-args].u.array,i); + if (s.type!=T_ARRAY) + error("Illegal contents of (root) array (image->apply_matrix)\n"); + if (width==-1) + width=s.u.array->size; + else + if (width!=s.u.array->size) + error("Arrays has different size (image->apply_matrix)\n"); + free_svalue(&s); + } + if (width==-1) width=0; + + matrix=malloc(sizeof(rgbd_group)*width*height+1); + if (!matrix) error("Out of memory"); + + for (i=0; i<height; i++) + { + struct svalue s,s2; + array_index_no_free(&s,sp[-args].u.array,i); + for (j=0; j<width; j++) + { + array_index_no_free(&s2,s.u.array,j); + if (s2.type==T_ARRAY && s2.u.array->size == 3) + { + struct svalue s3; + array_index_no_free(&s3,s2.u.array,0); + if (s3.type==T_INT) matrix[j+i*width].r=s3.u.integer; + else matrix[j+i*width].r=0; + free_svalue(&s3); + array_index_no_free(&s3,s2.u.array,1); + if (s3.type==T_INT) matrix[j+i*width].g=s3.u.integer; + else matrix[j+i*width].g=0; + free_svalue(&s3); + array_index_no_free(&s3,s2.u.array,2); + if (s3.type==T_INT) matrix[j+i*width].b=s3.u.integer; + else matrix[j+i*width].b=0; + free_svalue(&s3); + } + else if (s2.type==T_INT) + matrix[j+i*width].r=matrix[j+i*width].g= + matrix[j+i*width].b=s2.u.integer; + else + matrix[j+i*width].r=matrix[j+i*width].g= + matrix[j+i*width].b=0; + free_svalue(&s2); + } + free_svalue(&s2); + } + + o=clone(image_program,0); + +CHRONO("apply_matrix, begin"); + + if (THIS->img) + img_apply_matrix((struct image*)o->storage,THIS, + width,height,matrix,default_rgb,div); + +CHRONO("apply_matrix, end"); + + free(matrix); + + pop_n_elems(args); + push_object(o); +} + +void image_modify_by_intensity(INT32 args) +{ + INT32 x,y,i; + rgbl_group rgb; + rgb_group *list; + rgb_group *s,*d; + struct object *o; + struct image *img; + long div; + + if (!THIS->img) error("no image\n"); + if (args<5) + error("too few arguments to image->modify_by_intensity()\n"); + + getrgbl(&rgb,0,args,"image->modify_by_intensity()"); + div=rgb.r+rgb.g+rgb.b; + if (!div) div=1; + + s=malloc(sizeof(rgb_group)*(args-3)+1); + if (!s) error("Out of memory\n"); + + for (x=0; x<args-3; x++) + { + if (sp[3-args+x].type==T_INT) + s[x].r=s[x].g=s[x].b=testrange( sp[3-args+x].u.integer ); + else if (sp[3-args+x].type==T_ARRAY && + sp[3-args+x].u.array->size >= 3) + { + struct svalue sv; + array_index_no_free(&sv,sp[3-args+x].u.array,0); + if (sv.type==T_INT) s[x].r=testrange( sv.u.integer ); + else s[x].r=0; + array_index(&sv,sp[3-args+x].u.array,1); + if (sv.type==T_INT) s[x].g=testrange( sv.u.integer ); + else s[x].g=0; + array_index(&sv,sp[3-args+x].u.array,2); + if (sv.type==T_INT) s[x].b=testrange( sv.u.integer ); + else s[x].b=0; + free_svalue(&sv); + } + else s[x].r=s[x].g=s[x].b=0; + } + + list=malloc(sizeof(rgb_group)*256+1); + if (!list) + { + free(s); + error("out of memory\n"); + } + for (x=0; x<args-4; x++) + { + INT32 p1,p2,r; + p1=(255L*x)/(args-4); + p2=(255L*(x+1))/(args-4); + r=p2-p1; + for (y=0; y<r; y++) + { + list[y+p1].r=(((long)s[x].r)*(r-y)+((long)s[x+1].r)*(y))/r; + list[y+p1].g=(((long)s[x].g)*(r-y)+((long)s[x+1].g)*(y))/r; + list[y+p1].b=(((long)s[x].b)*(r-y)+((long)s[x+1].b)*(y))/r; + } + } + list[255]=s[x]; + free(s); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + d=img->img; + s=THIS->img; + + + x=THIS->xsize*THIS->ysize; + THREADS_ALLOW(); + while (x--) + { + i= testrange( ((((long)s->r)*rgb.r+ + ((long)s->g)*rgb.g+ + ((long)s->b)*rgb.b)/div) ); + *d=list[i]; + d++; + s++; + } + THREADS_DISALLOW(); + + free(list); + + pop_n_elems(args); + push_object(o); +} + +static void image_map_closest(INT32 args) +{ + struct colortable *ct; + long i; + rgb_group *d,*s; + struct object *o; + + if (!THIS->img) error("no image\n"); + if (args<1 + || sp[-args].type!=T_ARRAY) + error("illegal argument to image->map_closest()\n"); + + push_int(THIS->xsize); + push_int(THIS->ysize); + o=clone(image_program,2); + + ct=colortable_from_array(sp[-args].u.array,"image->map_closest()\n"); + pop_n_elems(args); + + i=THIS->xsize*THIS->ysize; + s=THIS->img; + d=((struct image*)(o->storage))->img; + THREADS_ALLOW(); + while (i--) + { + *d=ct->clut[colortable_rgb_nearest(ct,*s)]; + d++; *s++; + } + THREADS_DISALLOW(); + + colortable_free(ct); + push_object(o); +} + +static void image_map_fast(INT32 args) +{ + struct colortable *ct; + long i; + rgb_group *d,*s; + struct object *o; + + if (!THIS->img) error("no image\n"); + if (args<1 + || sp[-args].type!=T_ARRAY) + error("illegal argument to image->map_closest()\n"); + + push_int(THIS->xsize); + push_int(THIS->ysize); + o=clone(image_program,2); + + ct=colortable_from_array(sp[-args].u.array,"image->map_closest()\n"); + pop_n_elems(args); + + i=THIS->xsize*THIS->ysize; + s=THIS->img; + d=((struct image*)(o->storage))->img; + THREADS_ALLOW(); + while (i--) + { + *d=ct->clut[colortable_rgb(ct,*s)]; + d++; *s++; + } + THREADS_DISALLOW(); + + colortable_free(ct); + push_object(o); +} + +static void image_map_fs(INT32 args) +{ + struct colortable *ct; + INT32 i,j,xs; + rgb_group *d,*s; + struct object *o; + int *res,w; + rgbl_group *errb; + + if (!THIS->img) error("no image\n"); + if (args<1 + || sp[-args].type!=T_ARRAY) + error("illegal argument to image->map_fs()\n"); + + push_int(THIS->xsize); + push_int(THIS->ysize); + o=clone(image_program,2); + + res=(int*)xalloc(sizeof(int)*THIS->xsize); + errb=(rgbl_group*)xalloc(sizeof(rgbl_group)*THIS->xsize); + + ct=colortable_from_array(sp[-args].u.array,"image->map_closest()\n"); + pop_n_elems(args); + + for (i=0; i<THIS->xsize; i++) + errb[i].r=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].g=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].b=(rand()%(FS_SCALE*2+1))-FS_SCALE; + + i=THIS->ysize; + s=THIS->img; + d=((struct image*)(o->storage))->img; + w=0; + xs=THIS->xsize; + THREADS_ALLOW(); + while (i--) + { + image_floyd_steinberg(s,xs,errb,w=!w,res,ct); + for (j=0; j<THIS->xsize; j++) + *(d++)=ct->clut[res[j]]; + s+=xs; + } + THREADS_DISALLOW(); + + free(errb); + free(res); + colortable_free(ct); + push_object(o); +} + +void image_select_colors(INT32 args) +{ + struct colortable *ct; + int colors,i; + + if (args<1 + || sp[-args].type!=T_INT) + error("Illegal argument to image->select_colors()\n"); + + colors=sp[-args].u.integer; + pop_n_elems(args); + if (!THIS->img) { error("no image\n"); return; } + + ct=colortable_quant(THIS,colors); + for (i=0; i<colors; i++) + { + push_int(ct->clut[i].r); + push_int(ct->clut[i].g); + push_int(ct->clut[i].b); + f_aggregate(3); + } + f_aggregate(colors); + colortable_free(ct); +} + +/***************** global init etc *****************************/ + +#define RGB_TYPE "int|void,int|void,int|void,int|void" + +void init_font_programs(void); +void exit_font(void); + +void pike_module_init() +{ + int i; + + image_noise_init(); + + start_new_program(); + add_storage(sizeof(struct image)); + + add_function("create",image_create, + "function(int,int,"RGB_TYPE":void)",0); + add_function("clone",image_clone, + "function(int,int,"RGB_TYPE":object)",0); + add_function("new",image_clone, /* alias */ + "function(int,int,"RGB_TYPE":object)",0); + add_function("clear",image_clear, + "function("RGB_TYPE":object)",0); + add_function("toppm",image_toppm, + "function(:string)",0); + add_function("frompnm",image_frompnm, + "function(string:object|string)",0); + add_function("fromppm",image_frompnm, + "function(string:object|string)",0); + add_function("fromgif",image_fromgif, + "function(string:object)",0); + add_function("togif",image_togif, + "function(:string)",0); + add_function("togif_fs",image_togif_fs, + "function(:string)",0); + add_function("gif_begin",image_gif_begin, + "function(int:string)",0); + add_function("gif_add",image_gif_add, + "function(int|void,int|void:string)",0); + add_function("gif_add_fs",image_gif_add_fs, + "function(int|void,int|void:string)",0); + add_function("gif_add_nomap",image_gif_add_nomap, + "function(int|void,int|void:string)",0); + add_function("gif_add_fs_nomap",image_gif_add_fs_nomap, + "function(int|void,int|void:string)",0); + add_function("gif_end",image_gif_end, + "function(:string)",0); + add_function("gif_netscape_loop",image_gif_netscape_loop, + "function(:string)",0); + + add_function("copy",image_copy, + "function(void|int,void|int,void|int,void|int,"RGB_TYPE":object)",0); + add_function("autocrop",image_autocrop, + "function(void|int ...:object)",0); + add_function("scale",image_scale, + "function(int|float,int|float|void:object)",0); + add_function("translate",image_translate, + "function(int|float,int|float:object)",0); + add_function("translate_expand",image_translate_expand, + "function(int|float,int|float:object)",0); + + add_function("paste",image_paste, + "function(object,int|void,int|void:object)",0); + add_function("paste_alpha",image_paste_alpha, + "function(object,int,int|void,int|void:object)",0); + add_function("paste_mask",image_paste_mask, + "function(object,object,int|void,int|void:object)",0); + add_function("paste_alpha_color",image_paste_alpha_color, + "function(object,void|int,void|int,void|int,int|void,int|void:object)",0); + + add_function("setcolor",image_setcolor, + "function(int,int,int:object)",0); + add_function("setpixel",image_setpixel, + "function(int,int,"RGB_TYPE":object)",0); + add_function("getpixel",image_getpixel, + "function(int,int:array(int))",0); + add_function("line",image_line, + "function(int,int,int,int,"RGB_TYPE":object)",0); + add_function("circle",image_circle, + "function(int,int,int,int,"RGB_TYPE":object)",0); + add_function("box",image_box, + "function(int,int,int,int,"RGB_TYPE":object)",0); + add_function("tuned_box",image_tuned_box, + "function(int,int,int,int,array:object)",0); + + add_function("gray",image_gray, + "function("RGB_TYPE":object)",0); + add_function("color",image_color, + "function("RGB_TYPE":object)",0); + add_function("change_color",image_change_color, + "function(int,int,int,"RGB_TYPE":object)",0); + add_function("invert",image_invert, + "function("RGB_TYPE":object)",0); + add_function("threshold",image_threshold, + "function("RGB_TYPE":object)",0); + add_function("distancesq",image_distancesq, + "function("RGB_TYPE":object)",0); + add_function("select_from",image_select_from, + "function(int,int:object)",0); + + add_function("apply_matrix",image_apply_matrix, + "function(array(array(int|array(int))), void|int ...:object)",0); + add_function("modify_by_intensity",image_modify_by_intensity, + "function(int,int,int,int,int:object)",0); + + add_function("rotate_ccw",image_ccw, + "function(:object)",0); + add_function("rotate_cw",image_cw, + "function(:object)",0); + add_function("mirrorx",image_mirrorx, + "function(:object)",0); + add_function("mirrory",image_mirrory, + "function(:object)",0); + add_function("skewx",image_skewx, + "function(int|float,"RGB_TYPE":object)",0); + add_function("skewy",image_skewy, + "function(int|float,"RGB_TYPE":object)",0); + add_function("skewx_expand",image_skewx_expand, + "function(int|float,"RGB_TYPE":object)",0); + add_function("skewy_expand",image_skewy_expand, + "function(int|float,"RGB_TYPE":object)",0); + + add_function("rotate",image_rotate, + "function(int|float,"RGB_TYPE":object)",0); + add_function("rotate_expand",image_rotate_expand, + "function(int|float,"RGB_TYPE":object)",0); + + add_function("xsize",image_xsize, + "function(:int)",0); + add_function("ysize",image_ysize, + "function(:int)",0); + + add_function("map_closest",image_map_closest, + "function(:object)",0); + add_function("map_fast",image_map_fast, + "function(:object)",0); + add_function("map_fs",image_map_fs, + "function(:object)",0); + add_function("select_colors",image_select_colors, + "function(int:array(array(int)))",0); + + add_function("noise",image_noise, + "function(array(float|int|array(int)),float|void,float|void,float|void,float|void:object)",0); + add_function("turbulence",image_turbulence, + "function(array(float|int|array(int)),int|void,float|void,float|void,float|void,float|void:object)",0); + + add_function("dct",image_dct, + "function(:object)",0); + + add_function("`-",image_operator_minus, + "function(object|array(int):object)",0); + add_function("`+",image_operator_plus, + "function(object|array(int):object)",0); + add_function("`*",image_operator_multiply, + "function(object|array(int):object)",0); + add_function("`&",image_operator_minimum, + "function(object|array(int):object)",0); + add_function("`|",image_operator_maximum, + "function(object|array(int):object)",0); + + set_init_callback(init_image_struct); + set_exit_callback(exit_image_struct); + + image_program=end_program(); + add_program_constant("image",image_program, 0); + + for (i=0; i<CIRCLE_STEPS; i++) + circle_sin_table[i]=(INT32)4096*sin(((double)i)*2.0*3.141592653589793/(double)CIRCLE_STEPS); + + init_font_programs(); +} + +void pike_module_exit(void) +{ + if(image_program) + { + free_program(image_program); + image_program=0; + } + exit_font(); +} + + diff --git a/src/modules/Image/image.h b/src/modules/Image/image.h new file mode 100644 index 0000000000000000000000000000000000000000..e5828e3ef534907004af2d29ab149c9cf3d13f2d --- /dev/null +++ b/src/modules/Image/image.h @@ -0,0 +1,148 @@ +/* $Id: image.h,v 1.1 1997/02/11 08:35:43 hubbe Exp $ */ + +#define MAX_NUMCOL 32768 + +#define QUANT_SELECT_CACHE 6 + +#define COLOURTYPE unsigned char + +#define FS_SCALE 1024 + +typedef struct +{ + COLOURTYPE r,g,b; +} rgb_group; + +typedef struct +{ + unsigned char r,g,b,alpha; +} rgba_group; + +typedef struct +{ + INT32 r,g,b; +} rgbl_group; + +typedef struct +{ + float r,g,b; +} rgbd_group; /* use float, it gets so big otherwise... */ + +struct image +{ + rgb_group *img; + INT32 xsize,ysize; + rgb_group rgb; + unsigned char alpha; +}; + +struct colortable +{ + int numcol; + struct rgb_cache + { + rgb_group index; + int value; + } cache[QUANT_SELECT_CACHE]; + unsigned long *rgb_node; +/* bit + 31..30 29..22 21..0 + 0=color split value + 1=split red on this + 2=split green =x <=x >x + 3=split blue value value+1 + It will fail for more than 2097152 colors. Sorry... *grin* + */ + rgb_group clut[1]; +}; /* rgb_node follows, 2*numcol */ + + +/* colortable declarations - from quant */ + +struct colortable *colortable_quant(struct image *img,int numcol); +int colortable_rgb(struct colortable *ct,rgb_group rgb); +int colortable_rgb_nearest(struct colortable *ct,rgb_group rgb); +void colortable_free(struct colortable *ct); +struct colortable *colortable_from_array(struct array *arr,char *from); + +/* encoding of a gif - from togif */ + +struct pike_string * + image_encode_gif(struct image *img,struct colortable *ct, + rgb_group *transparent, + int floyd_steinberg); +void image_floyd_steinberg(rgb_group *rgb,int xsize, + rgbl_group *errl, + int way,int *res, + struct colortable *ct); + +int image_decode_gif(struct image *dest,struct image *dest_alpha, + unsigned char *src,unsigned long len); + +void image_togif(INT32 args); +void image_togif_fs(INT32 args); +void image_fromgif(INT32 args); +void image_gif_begin(INT32 args); +void image_gif_add(INT32 args); +void image_gif_add_fs(INT32 args); +void image_gif_add_nomap(INT32 args); +void image_gif_add_fs_nomap(INT32 args); +void image_gif_end(INT32 args); +void image_gif_netscape_loop(INT32 args); + +/* blit.c */ + +void img_clear(rgb_group *dest,rgb_group rgb,INT32 size); +void img_box_nocheck(INT32 x1,INT32 y1,INT32 x2,INT32 y2); +void img_box(INT32 x1,INT32 y1,INT32 x2,INT32 y2); +void img_blit(rgb_group *dest,rgb_group *src,INT32 width, + INT32 lines,INT32 moddest,INT32 modsrc); +void img_crop(struct image *dest, + struct image *img, + INT32 x1,INT32 y1, + INT32 x2,INT32 y2); +void img_clone(struct image *newimg,struct image *img); +void image_paste(INT32 args); +void image_paste_alpha(INT32 args); +void image_paste_mask(INT32 args); +void image_paste_alpha_color(INT32 args); + +/* matrix.c */ + +void image_scale(INT32 args); +void image_translate(INT32 args); +void image_translate_expand(INT32 args); +void image_skewx(INT32 args); +void image_skewy(INT32 args); +void image_skewx_expand(INT32 args); +void image_skewy_expand(INT32 args); +void image_rotate(INT32 args); +void image_rotate_expand(INT32 args); +void image_cw(INT32 args); +void image_ccw(INT32 args); +void image_ccw(INT32 args); +void image_mirrorx(INT32 args); +void image_mirrory(INT32 args); + +/* pnm.c */ + +void image_toppm(INT32 args); +void image_frompnm(INT32 args); + +/* pattern.c */ + +void image_noise(INT32 args); +void image_turbulence(INT32 args); +void image_noise_init(void); + +/* dct.c */ + +void image_dct(INT32 args); + +/* operator.c */ + +void image_operator_minus(INT32 args); +void image_operator_plus(INT32 args); +void image_operator_multiply(INT32 args); +void image_operator_maximum(INT32 args); +void image_operator_minimum(INT32 args); diff --git a/src/modules/Image/lzw.c b/src/modules/Image/lzw.c new file mode 100644 index 0000000000000000000000000000000000000000..31e01592ef5734c352292c516fff1aff693a13bf --- /dev/null +++ b/src/modules/Image/lzw.c @@ -0,0 +1,368 @@ +/* $Id: lzw.c,v 1.1 1997/02/11 08:35:44 hubbe Exp $ */ + +/* + +lzw, used by togif + +Pontus Hagland, law@infovav.se + +this file can be used generally for lzw algoritms, +the existanse of #define GIF_LZW is for that purpose. :-) +/Pontus + +*/ + +#include "global.h" + +#include "lzw.h" + +#define DEFAULT_OUTBYTES 16384 +#ifndef GIF_LZW +#define STDLZWCODES 8192 +#endif + +static INLINE void lzw_output(struct lzw *lzw,lzwcode_t codeno) +{ + int bits,bitp; + unsigned char c; + +/* + fprintf(stderr,"%03x bits=%d codes %d %c\n", + codeno,lzw->codebits,lzw->codes+1, + (codeno==lzw->codes+1) ? '=' : ' '); + */ +#if 0 + fprintf(stderr,"\nwrote %4d<'",codeno); + lzw_recurse_find_code(lzw,codeno); + fprintf(stderr,"' "); +#endif + + if (lzw->outpos+4>=lzw->outlen) + lzw->out=realloc(lzw->out,lzw->outlen*=2); + + bitp=lzw->outbit; + c=lzw->lastout; + bits=lzw->codebits; +#ifdef GIF_LZW + if (bits>12) bits=12; +#endif + + while (bits) + { + c|=(codeno<<bitp); + if (bits+bitp>=8) + { + bits-=8-bitp; + codeno>>=8-bitp; + bitp=0; + lzw->out[lzw->outpos++]=c; + c=0; + } + else + { + lzw->outbit=bitp+bits; + lzw->lastout=c; + return; + } + } + lzw->lastout=0; + lzw->outbit=0; +} + + +void lzw_init(struct lzw *lzw,int bits) +{ + unsigned long i; +#ifdef GIF_LZW + lzw->codes=(1L<<bits)+2; +#else + lzw->codes=(1L<<bits); +#endif + lzw->bits=bits; + lzw->codebits=bits+1; + lzw->code=(struct lzwc*) malloc(sizeof(struct lzwc)* +#ifdef GIF_LZW + 4096 +#else + (lzw->alloced=STDLZWCODES) +#endif + ); + + for (i=0; i<lzw->codes; i++) + { + lzw->code[i].c=(unsigned char)i; + lzw->code[i].firstchild=LZWCNULL; + lzw->code[i].next=LZWCNULL; + } + lzw->out=malloc(DEFAULT_OUTBYTES); + lzw->outlen=DEFAULT_OUTBYTES; + lzw->outpos=0; + lzw->current=LZWCNULL; + lzw->outbit=0; + lzw->lastout=0; +#ifdef GIF_LZW + lzw_output(lzw,1L<<bits); +#endif +} + +void lzw_quit(struct lzw *lzw) +{ + int i; + + free(lzw->code); + free(lzw->out); +} + +#if 0 +static void lzw_recurse_find_code(struct lzw *lzw,lzwcode_t codeno) +{ + lzwcode_t i; + if (codeno<(1L<<lzw->bits)) + fprintf(stderr,"%c",codeno); + else if (codeno==(1L<<lzw->bits)) + fprintf(stderr,"(clear)"); + else if (codeno==(1L<<lzw->bits)+1) + fprintf(stderr,"(end)"); + else for (;;) + { + /* direct child? */ + for (i=0; i<lzw->codes; i++) + if (lzw->code[i].firstchild==codeno) + { + lzw_recurse_find_code(lzw,i); + fprintf(stderr,"%c",lzw->code[codeno].c); + return; + } + for (i=0; i<lzw->codes; i++) + if (lzw->code[i].next==codeno) codeno=i; + } +} +#endif + +void lzw_write_last(struct lzw *lzw) +{ + if (lzw->current) + lzw_output(lzw,lzw->current); +#ifdef GIF_LZW + lzw_output( lzw, (1L<<lzw->bits)+1 ); /* GIF end code */ +#endif + if (lzw->outbit) + lzw->out[lzw->outpos++]=lzw->lastout; +} + +void lzw_add(struct lzw *lzw,int c) +{ + lzwcode_t lno,lno2; + struct lzwc *l; + + if (lzw->current==LZWCNULL) /* no current, load */ + { + lzw->current=c; + return; + } + + lno=lzw->code[lzw->current].firstchild; /* check if we have this sequence */ + while (lno!=LZWCNULL) + { + if (lzw->code[lno].c==c && lno!=lzw->codes-1 ) + { + lzw->current=lno; + return; + } + lno=lzw->code[lno].next; + } + + +#ifdef GIF_LZW + if (lzw->codes==4096) /* needs more than 12 bits */ + { + int i; + + lzw_output(lzw,lzw->current); + + for (i=0; i<(1L<<lzw->bits); i++) + lzw->code[i].firstchild=LZWCNULL; + lzw->codes=(1L<<lzw->bits)+2; + + /* output clearcode, 1000... (bits) */ + lzw_output(lzw,1L<<lzw->bits); + + lzw->codebits=lzw->bits+1; + lzw->current=c; + return; + } +#else + if (lzw->codes==MAXLZWCODES) + realloc lzwc->code... move lzw->current +#endif + + /* output current code no, make new & reset */ + + lzw_output(lzw,lzw->current); + + lno=lzw->code[lzw->current].firstchild; + lno2=lzw->codes; + l=lzw->code+lno2; + l->next=lno; + l->firstchild=LZWCNULL; + l->c=c; + lzw->code[lzw->current].firstchild=lno2; + + lzw->codes++; + if (lzw->codes>(unsigned long)(1L<<lzw->codebits)) lzw->codebits++; + + lzw->current=c; +} + +#undef UNPACK_DEBUG + +#ifdef GIF_LZW +unsigned long lzw_unpack(unsigned char *dest,unsigned long destlen, + unsigned char *src,unsigned long srclen, + int bits) +{ + struct lzwuc + { + unsigned char c; + lzwcode_t parent; + lzwcode_t len; /* no string is longer then that ... */ + } *code; + + static unsigned short mask[16]={0,1,3,7,017,037,077,0177,0377,0777,01777, + 03777,07777,01777,03777,07777}; + + unsigned long wrote=0; + int i,cbits,cbit,clear=(1<<bits),end=(1<<bits)+1; + lzwcode_t current,last,used,nextlast; + unsigned long store; + unsigned char *srcend=src+srclen,*destend=dest+destlen; + unsigned char first=0; + + code=malloc(sizeof(struct lzwuc)*4096); + if (!code) return 0; + + for (i=0; i<(1<<bits); i++) + { + code[i].c=i; + code[i].parent=LZWCNULL; + code[i].len=1; + } + + cbit=0; + cbits=bits+1; + store=0; + last=LZWCNULL; + used=end+1; + + while (src!=srcend) + { + if (cbit>cbits) + { + current=store>>(32-cbits); + cbit-=cbits; + } + else + { + while (cbits-cbit>=8) + { + store=(store>>8)|((*(src++))<<24); + cbit+=8; + if (src==srcend) { free(code); return wrote; } + } + store=(store>>8)|((*(src++))<<24); + cbit+=8; + current=(store>>(32-(cbit)))&mask[cbits]; + cbit-=cbits; + } + +#ifdef UNPACK_DEBUG + fprintf(stderr,"%03x ",current); +#endif + + if (current==clear) /* clear tree */ + { + for (i=0; i<end-2; i++) + code[i].parent=LZWCNULL; + last=LZWCNULL; + used=end+1; + if (cbits!=bits+1) + { + cbits=bits+1; + cbit++; + } + } + else if (current==end) /* end of data */ + break; + else if (last==LZWCNULL) + { + last=current; + if (last>end) break; + *(dest++)=(unsigned char)current; + wrote++; + first=current; + } + else + { + lzwcode_t n; + unsigned char *dest2; + + if (code[current].len+dest>destend) /* no space, cancel */ + break; + + nextlast=current; + + if (current>=used) + { + *(dest++)=(unsigned char)first; + wrote++; + current=last; + } + + dest+=code[current].len; + wrote+=code[current].len; + + dest2=dest; + n=current; + *--dest2=code[n].c; + while (n>end) + { + n=code[n].parent; + *--dest2=code[n].c; + } + + +/* + *dest=0; + fprintf(stderr,"read %4d>'",current); + for (i=0; i<code[current].len; i++) + fprintf(stderr,"%c",(dest-code[current].len)[i]); + fprintf(stderr,"' == "); + for (i=0; i<code[current].len; i++) + fprintf(stderr,"%02x",(dest-code[current].len)[i]); + fprintf(stderr,"\n"); +*/ + + if (used<4096) + { + code[used].c=code[n].c; + code[used].parent=last; + code[used].len=code[last].len+1; + used++; + + if (used>=(1<<cbits)) + { + cbits++; +#ifdef UNPACK_DEBUG + fprintf(stderr,"[%d bits]",cbits); +#endif + } + } + + last=nextlast; + } + } + free(code); + return wrote; +} +#endif + diff --git a/src/modules/Image/lzw.h b/src/modules/Image/lzw.h new file mode 100644 index 0000000000000000000000000000000000000000..2b8f3030309c168770e6063aa061f00f646cfc99 --- /dev/null +++ b/src/modules/Image/lzw.h @@ -0,0 +1,40 @@ +/* $Id: lzw.h,v 1.1 1997/02/11 08:35:44 hubbe Exp $ */ + +#define GIF_LZW + +#ifdef GIF_LZW +typedef unsigned short lzwcode_t; /* no more than 12 bits used */ +#else +typedef unsigned long lzwcode_t; +#endif + +struct lzw +{ + unsigned long codes; + unsigned long bits; /* initial encoding bits */ + unsigned long codebits; /* current encoding bits */ + unsigned long outlen,outpos,outbit; + unsigned char *out,lastout; + struct lzwc + { + unsigned char c; + lzwcode_t firstchild; + lzwcode_t next; + } *code; + lzwcode_t current,firstfree; +#ifndef GIF_LZW + unsigned long alloced; +#endif +}; + +#define LZWCNULL ((lzwcode_t)(~0)) + +void lzw_add(struct lzw *lzw,int c); +void lzw_quit(struct lzw *lzw); +void lzw_init(struct lzw *lzw,int bits); +unsigned long lzw_unpack(unsigned char *dest,unsigned long destlen, + unsigned char *src,unsigned long srclen, + int bits); + + + diff --git a/src/modules/Image/matrix.c b/src/modules/Image/matrix.c new file mode 100644 index 0000000000000000000000000000000000000000..4b207261b4d50b854f9c7e178b57b89f5528eec5 --- /dev/null +++ b/src/modules/Image/matrix.c @@ -0,0 +1,977 @@ +/* $Id: matrix.c,v 1.1 1997/02/11 08:35:44 hubbe Exp $ */ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "threads.h" +#include "error.h" + +#include "image.h" + +extern struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) + +#if 0 +#include <sys/resource.h> +#define CHRONO(X) chrono(X) + +static void chrono(char *x) +{ + struct rusage r; + static struct rusage rold; + getrusage(RUSAGE_SELF,&r); + fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x, + r.ru_utime.tv_sec,r.ru_utime.tv_usec, + + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0) + +r.ru_utime.tv_sec-rold.ru_utime.tv_sec, + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0) + + r.ru_utime.tv_usec-rold.ru_utime.tv_usec + ); + + rold=r; +} +#else +#define CHRONO(X) +#endif + +/***************** internals ***********************************/ + +#define apply_alpha(x,y,alpha) \ + ((unsigned char)((y*(255L-(alpha))+x*(alpha))/255L)) + +#define set_rgb_group_alpha(dest,src,alpha) \ + ((dest).r=apply_alpha((dest).r,(src).r,alpha), \ + (dest).g=apply_alpha((dest).g,(src).g,alpha), \ + (dest).b=apply_alpha((dest).b,(src).b,alpha)) + +#define pixel(_img,x,y) ((_img)->img[(x)+(y)*(_img)->xsize]) + +#define setpixel(x,y) \ + (THIS->alpha? \ + set_rgb_group_alpha(THIS->img[(x)+(y)*THIS->xsize],THIS->rgb,THIS->alpha): \ + ((pixel(THIS,x,y)=THIS->rgb),0)) + +#define setpixel_test(x,y) \ + (((x)<0||(y)<0||(x)>=THIS->xsize||(y)>=THIS->ysize)? \ + 0:(setpixel(x,y),0)) + +static INLINE int getrgb(struct image *img, + INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return 0; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + img->rgb.r=(unsigned char)sp[-args+args_start].u.integer; + img->rgb.g=(unsigned char)sp[1-args+args_start].u.integer; + img->rgb.b=(unsigned char)sp[2-args+args_start].u.integer; + if (args-args_start>=4) + if (sp[3-args+args_start].type!=T_INT) + error("Illegal alpha argument to %s\n",name); + else + img->alpha=sp[3-args+args_start].u.integer; + else + img->alpha=0; + return 1; +} + +static INLINE int getrgbl(rgbl_group *rgb,INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return 0; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + rgb->r=sp[-args+args_start].u.integer; + rgb->g=sp[1-args+args_start].u.integer; + rgb->b=sp[2-args+args_start].u.integer; + return 1; +} + +/** end internals **/ + + +#define decimals(x) ((x)-(int)(x)) +#define testrange(x) max(min((x),255),0) +#define _scale_add_rgb(dest,src,factor) \ + ((dest)->r+=(src)->r*(factor), \ + (dest)->g+=(src)->g*(factor), \ + (dest)->b+=(src)->b*(factor)) +#define scale_add_pixel(dest,dx,src,sx,factor) \ + _scale_add_rgb(dest,src,factor) + +static INLINE void scale_add_line(rgbd_group *new,INT32 yn,INT32 newx, + rgb_group *img,INT32 y,INT32 xsize, + double py,double dx) +{ + INT32 x,xd; + double xn,xndxd; + new=new+yn*newx; + img=img+y*xsize; + for (x=0,xn=0; x<xsize; img++,x++,xn+=dx) + { + if ((INT32)xn<(INT32)(xn+dx)) + { + xndxd=py*(1.0-decimals(xn)); + if (xndxd) + scale_add_pixel(new,(INT32)xn,img,x,xndxd); + if (dx>=1.0 && (xd=(INT32)(xn+dx)-(INT32)(xn))>1) + while (--xd) + { + new++; + scale_add_pixel(new,(INT32)(xn+xd),img,x,py); + } + xndxd=py*decimals(xn+dx); + new++; + if (xndxd) + scale_add_pixel(new,(INT32)(xn+dx),img,x,xndxd); + } + else + scale_add_pixel(new,(int)xn,img,x,py*dx); + } +} + +void img_scale(struct image *dest, + struct image *source, + INT32 newx,INT32 newy) +{ + rgbd_group *new,*s; + rgb_group *d; + INT32 y,yd; + double yn,dx,dy; + +CHRONO("scale begin"); + + if (dest->img) { free(dest->img); dest->img=NULL; } + + if (!THIS->img || newx<=0 || newy<=0) return; /* no way */ + + THREADS_ALLOW(); + new=malloc(newx*newy*sizeof(rgbd_group) +1); + if (!new) error("Out of memory!\n"); + + for (y=0; y<newx*newy; y++) + new[y].r=new[y].g=new[y].b=0.0; + + dx=((double)newx-0.000001)/source->xsize; + dy=((double)newy-0.000001)/source->ysize; + + for (y=0,yn=0; y<source->ysize; y++,yn+=dy) + { + if ((INT32)yn<(INT32)(yn+dy)) + { + if (1.0-decimals(yn)) + scale_add_line(new,(INT32)(yn),newx,source->img,y,source->xsize, + (1.0-decimals(yn)),dx); + if ((yd=(INT32)(yn+dy)-(INT32)(yn))>1) + while (--yd) + scale_add_line(new,(INT32)yn+yd,newx,source->img,y,source->xsize, + 1.0,dx); + if (decimals(yn+dy)) + scale_add_line(new,(INT32)(yn+dy),newx,source->img,y,source->xsize, + (decimals(yn+dy)),dx); + } + else + scale_add_line(new,(INT32)yn,newx,source->img,y,source->xsize, + dy,dx); + } + + dest->img=d=malloc(newx*newy*sizeof(rgb_group) +1); + if (!d) { free(new); error("Out of memory!\n"); } + +CHRONO("transfer begin"); + + s=new; + y=newx*newy; + while (y--) + { + d->r=min((int)(s->r+0.5),255); + d->g=min((int)(s->g+0.5),255); + d->b=min((int)(s->b+0.5),255); + d++; s++; + } + + dest->xsize=newx; + dest->ysize=newy; + + free(new); + +CHRONO("scale end"); + THREADS_DISALLOW(); +} + +/* Special, faster, case for scale=1/2 */ +void img_scale2(struct image *dest, struct image *source) +{ + rgb_group *new; + INT32 x, y, newx, newy; + newx = source->xsize >> 1; + newy = source->ysize >> 1; + + if (dest->img) { free(dest->img); dest->img=NULL; } + if (!THIS->img || newx<=0 || newy<=0) return; /* no way */ + + THREADS_ALLOW(); + new=malloc(newx*newy*sizeof(rgb_group) +1); + if (!new) error("Out of memory\n"); + MEMSET(new,0,newx*newy*sizeof(rgb_group)); + + dest->img=new; + dest->xsize=newx; + dest->ysize=newy; + for (y = 0; y < newy; y++) + for (x = 0; x < newx; x++) { + pixel(dest,x,y).r = (COLOURTYPE) + (((INT32) pixel(source,2*x+0,2*y+0).r+ + (INT32) pixel(source,2*x+1,2*y+0).r+ + (INT32) pixel(source,2*x+0,2*y+1).r+ + (INT32) pixel(source,2*x+1,2*y+1).r) >> 2); + pixel(dest,x,y).g = (COLOURTYPE) + (((INT32) pixel(source,2*x+0,2*y+0).g+ + (INT32) pixel(source,2*x+1,2*y+0).g+ + (INT32) pixel(source,2*x+0,2*y+1).g+ + (INT32) pixel(source,2*x+1,2*y+1).g) >> 2); + pixel(dest,x,y).b = (COLOURTYPE) + (((INT32) pixel(source,2*x+0,2*y+0).b+ + (INT32) pixel(source,2*x+1,2*y+0).b+ + (INT32) pixel(source,2*x+0,2*y+1).b+ + (INT32) pixel(source,2*x+1,2*y+1).b) >> 2); + } + THREADS_DISALLOW(); +} + + + +void image_scale(INT32 args) +{ + float factor; + struct object *o; + struct image *newimg; + + o=clone(image_program,0); + newimg=(struct image*)(o->storage); + + if (args==1 && sp[-args].type==T_FLOAT) { + if (sp[-args].u.float_number == 0.5) + img_scale2(newimg,THIS); + else + img_scale(newimg,THIS, + (INT32)(THIS->xsize*sp[-args].u.float_number), + (INT32)(THIS->ysize*sp[-args].u.float_number)); + } + else if (args>=2 && + sp[-args].type==T_INT && sp[-args].u.integer==0 && + sp[1-args].type==T_INT) + { + factor=((float)sp[1-args].u.integer)/THIS->ysize; + img_scale(newimg,THIS, + (INT32)(THIS->xsize*factor), + sp[1-args].u.integer); + } + else if (args>=2 && + sp[1-args].type==T_INT && sp[1-args].u.integer==0 && + sp[-args].type==T_INT) + { + factor=((float)sp[-args].u.integer)/THIS->xsize; + img_scale(newimg,THIS, + sp[-args].u.integer, + (INT32)(THIS->ysize*factor)); + } + else if (args>=2 && + sp[-args].type==T_FLOAT && + sp[1-args].type==T_FLOAT) + img_scale(newimg,THIS, + (INT32)(THIS->xsize*sp[-args].u.float_number), + (INT32)(THIS->ysize*sp[1-args].u.float_number)); + else if (args>=2 && + sp[-args].type==T_INT && + sp[1-args].type==T_INT) + img_scale(newimg,THIS, + sp[-args].u.integer, + sp[1-args].u.integer); + else + { + free_object(o); + error("illegal arguments to image->scale()\n"); + } + pop_n_elems(args); + push_object(o); +} + +void image_ccw(INT32 args) +{ + INT32 i,j,xs,ys; + rgb_group *src,*dest; + struct object *o; + struct image *img; + + pop_n_elems(args); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + img->xsize=THIS->ysize; + img->ysize=THIS->xsize; + i=xs=THIS->xsize; + ys=THIS->ysize; + src=THIS->img+THIS->xsize-1; + dest=img->img; + + THREADS_ALLOW(); + while (i--) + { + j=ys; + while (j--) *(dest++)=*(src),src+=xs; + src--; + src-=xs*ys; + } + THREADS_DISALLOW(); + + push_object(o); +} + +static void img_cw(struct image *is,struct image *id) +{ + INT32 i,j; + rgb_group *src,*dest; + + if (id->img) free(id->img); + *id=*is; + if (!(id->img=malloc(sizeof(rgb_group)*is->xsize*is->ysize+1))) + error("Out of memory\n"); + + id->xsize=is->ysize; + id->ysize=is->xsize; + i=is->xsize; + src=is->img+is->xsize-1; + dest=id->img; + THREADS_ALLOW(); + while (i--) + { + j=is->ysize; + while (j--) *(dest++)=*(src),src+=is->xsize; + src--; + src-=is->xsize*is->ysize; + } + THREADS_DISALLOW(); +} + +void img_ccw(struct image *is,struct image *id) +{ + INT32 i,j; + rgb_group *src,*dest; + + if (id->img) free(id->img); + *id=*is; + if (!(id->img=malloc(sizeof(rgb_group)*is->xsize*is->ysize+1))) + error("Out of memory\n"); + + id->xsize=is->ysize; + id->ysize=is->xsize; + i=is->xsize; + src=is->img+is->xsize-1; + dest=id->img+is->xsize*is->ysize; + THREADS_ALLOW(); + while (i--) + { + j=is->ysize; + while (j--) *(--dest)=*(src),src+=is->xsize; + src--; + src-=is->xsize*is->ysize; + } + THREADS_DISALLOW(); +} + +void image_cw(INT32 args) +{ + INT32 i,j,xs,ys; + rgb_group *src,*dest; + struct object *o; + struct image *img; + + pop_n_elems(args); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + ys=img->xsize=THIS->ysize; + i=xs=img->ysize=THIS->xsize; + + src=THIS->img+THIS->xsize-1; + dest=img->img+THIS->xsize*THIS->ysize; + THREADS_ALLOW(); + while (i--) + { + j=ys; + while (j--) *(--dest)=*(src),src+=xs; + src--; + src-=xs*ys; + } + THREADS_DISALLOW(); + + push_object(o); +} + +void image_mirrorx(INT32 args) +{ + rgb_group *src,*dest; + struct object *o; + struct image *img; + INT32 i,j,xs; + + pop_n_elems(args); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + i=THIS->ysize; + src=THIS->img+THIS->xsize-1; + dest=img->img; + xs=THIS->xsize; + THREADS_ALLOW(); + while (i--) + { + j=xs; + while (j--) *(dest++)=*(src--); + src+=xs*2; + } + THREADS_DISALLOW(); + + push_object(o); +} + +void image_mirrory(INT32 args) +{ + rgb_group *src,*dest; + struct object *o; + struct image *img; + INT32 i,j,xs; + + pop_n_elems(args); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + i=THIS->ysize; + src=THIS->img+THIS->xsize*(THIS->ysize-1); + dest=img->img; + xs=THIS->xsize; + THREADS_ALLOW(); + while (i--) + { + j=xs; + while (j--) *(dest++)=*(src++); + src-=xs*2; + } + THREADS_DISALLOW(); + + push_object(o); + +} + + +#define ROUND(X) ((unsigned char)((X)+0.5)) + +static void img_skewx(struct image *src, + struct image *dest, + float diff, + int xpn) /* expand pixel for use with alpha instead */ +{ + double x0,xmod,xm; + INT32 y,x,len; + rgb_group *s,*d; + rgb_group rgb; + + if (dest->img) free(dest->img); + if (diff<0) + dest->xsize=ceil(-diff)+src->xsize,x0=-diff; + else + dest->xsize=ceil(diff)+src->xsize,x0=0; + dest->ysize=src->ysize; + len=src->xsize; + + d=dest->img=malloc(sizeof(rgb_group)*dest->xsize*dest->ysize); + if (!d) return; + s=src->img; + + THREADS_ALLOW(); + xmod=diff/src->ysize; + rgb=dest->rgb; + + CHRONO("skewx begin\n"); + + y=src->ysize; + while (y--) + { + int j; + if (xpn) rgb=*s; + for (j=x0; j--;) *(d++)=rgb; + if (!(xm=(x0-floor(x0)))) + { + for (j=len; j--;) *(d++)=*(s++); + j=dest->xsize-x0-len; + } + else + { + float xn=1-xm; + if (xpn) + *d=*s; + else + d->r=ROUND(rgb.r*xm+s->r*xn), + d->g=ROUND(rgb.g*xm+s->g*xn), + d->b=ROUND(rgb.b*xm+s->b*xn); + d++; + for (j=len-1; j--;) + { + d->r=ROUND(s->r*xm+s[1].r*xn), + d->g=ROUND(s->g*xm+s[1].g*xn), + d->b=ROUND(s->b*xm+s[1].b*xn); + d++; + s++; + } + if (xpn) + *d=*s; + else + d->r=ROUND(rgb.r*xn+s->r*xm), + d->g=ROUND(rgb.g*xn+s->g*xm), + d->b=ROUND(rgb.b*xn+s->b*xm); + d++; + s++; + j=dest->xsize-x0-len; + } + if (xpn) rgb=s[-1]; + while (j--) *(d++)=rgb; + x0+=xmod; + } + THREADS_DISALLOW(); + + CHRONO("skewx end\n"); +} + +static void img_skewy(struct image *src, + struct image *dest, + float diff, + int xpn) /* expand pixel for use with alpha instead */ +{ + double y0,ymod,ym; + INT32 y,x,len,xsz; + rgb_group *s,*d; + rgb_group rgb; + + if (dest->img) free(dest->img); + if (diff<0) + dest->ysize=ceil(-diff)+src->ysize,y0=-diff; + else + dest->ysize=ceil(diff)+src->ysize,y0=0; + xsz=dest->xsize=src->xsize; + len=src->ysize; + + d=dest->img=malloc(sizeof(rgb_group)*dest->ysize*dest->xsize); + if (!d) return; + s=src->img; + + THREADS_ALLOW(); + ymod=diff/src->xsize; + rgb=dest->rgb; + +CHRONO("skewy begin\n"); + + x=src->xsize; + while (x--) + { + int j; + if (xpn) rgb=*s; + for (j=y0; j--;) *d=rgb,d+=xsz; + if (!(ym=(y0-floor(y0)))) + { + for (j=len; j--;) *d=*s,d+=xsz,s+=xsz; + j=dest->ysize-y0-len; + } + else + { + float yn=1-ym; + if (xpn) + *d=*s; + else + d->r=ROUND(rgb.r*ym+s->r*yn), + d->g=ROUND(rgb.g*ym+s->g*yn), + d->b=ROUND(rgb.b*ym+s->b*yn); + d+=xsz; + for (j=len-1; j--;) + { + d->r=ROUND(s->r*ym+s[xsz].r*yn), + d->g=ROUND(s->g*ym+s[xsz].g*yn), + d->b=ROUND(s->b*ym+s[xsz].b*yn); + d+=xsz; + s+=xsz; + } + if (xpn) + *d=*s; + else + d->r=ROUND(rgb.r*yn+s->r*ym), + d->g=ROUND(rgb.g*yn+s->g*ym), + d->b=ROUND(rgb.b*yn+s->b*ym); + d+=xsz; + s+=xsz; + j=dest->ysize-y0-len; + } + if (xpn) rgb=s[-xsz]; + while (j--) *d=rgb,d+=xsz; + s-=len*xsz-1; + d-=dest->ysize*xsz-1; + y0+=ymod; + } + THREADS_DISALLOW(); + +CHRONO("skewy end\n"); + +} + +void image_skewx(INT32 args) +{ + float diff=0; + struct object *o; + + if (args<1) + error("too few arguments to image->skewx()\n"); + else if (sp[-args].type==T_FLOAT) + diff=THIS->ysize*sp[-args].u.float_number; + else if (sp[-args].type==T_INT) + diff=sp[-args].u.integer; + else + error("illegal argument to image->skewx()\n"); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + + if (!getrgb((struct image*)(o->storage),1,args,"image->skewx()")) + ((struct image*)(o->storage))->rgb=THIS->rgb; + + img_skewx(THIS,(struct image*)(o->storage),diff,0); + + pop_n_elems(args); + push_object(o); +} + +void image_skewy(INT32 args) +{ + float diff=0; + struct object *o; + + if (args<1) + error("too few arguments to image->skewy()\n"); + else if (sp[-args].type==T_FLOAT) + diff=THIS->xsize*sp[-args].u.float_number; + else if (sp[-args].type==T_INT) + diff=sp[-args].u.integer; + else + error("illegal argument to image->skewx()\n"); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + + if (!getrgb((struct image*)(o->storage),1,args,"image->skewy()")) + ((struct image*)(o->storage))->rgb=THIS->rgb; + + img_skewy(THIS,(struct image*)(o->storage),diff,0); + + pop_n_elems(args); + push_object(o); +} + +void image_skewx_expand(INT32 args) +{ + float diff=0; + struct object *o; + + if (args<1) + error("too few arguments to image->skewx()\n"); + else if (sp[-args].type==T_FLOAT) + diff=THIS->ysize*sp[-args].u.float_number; + else if (sp[-args].type==T_INT) + diff=sp[-args].u.integer; + else + error("illegal argument to image->skewx()\n"); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + + if (!getrgb((struct image*)(o->storage),1,args,"image->skewx()")) + ((struct image*)(o->storage))->rgb=THIS->rgb; + + img_skewx(THIS,(struct image*)(o->storage),diff,1); + + pop_n_elems(args); + push_object(o); +} + +void image_skewy_expand(INT32 args) +{ + float diff=0; + struct object *o; + + if (args<1) + error("too few arguments to image->skewy()\n"); + else if (sp[-args].type==T_FLOAT) + diff=THIS->xsize*sp[-args].u.float_number; + else if (sp[-args].type==T_INT) + diff=sp[-args].u.integer; + else + error("illegal argument to image->skewx()\n"); + + if (!THIS->img) error("no image\n"); + + o=clone(image_program,0); + + if (!getrgb((struct image*)(o->storage),1,args,"image->skewy()")) + ((struct image*)(o->storage))->rgb=THIS->rgb; + + img_skewy(THIS,(struct image*)(o->storage),diff,1); + + pop_n_elems(args); + push_object(o); +} + + + +void img_rotate(INT32 args,int xpn) +{ + float angle=0; + struct object *o; + struct image *dest,d0,dest2; + + if (args<1) + error("too few arguments to image->rotate()\n"); + else if (sp[-args].type==T_FLOAT) + angle=sp[-args].u.float_number; + else if (sp[-args].type==T_INT) + angle=sp[-args].u.integer; + else + error("illegal argument to image->rotate()\n"); + + if (!THIS->img) error("no image\n"); + + dest2.img=d0.img=NULL; + + if (angle<-135) angle-=360*(int)((angle-225)/360); + else if (angle>225) angle-=360*(int)((angle+135)/360); + if (angle<-45) + { + img_ccw(THIS,&dest2); + angle+=90; + } + else if (angle>135) + { + img_ccw(THIS,&d0); + img_ccw(&d0,&dest2); + angle-=180; + } + else if (angle>45) + { + img_cw(THIS,&dest2); + angle-=90; + } + else dest2=*THIS; + + angle=(angle/180.0)*3.141592653589793; + + o=clone(image_program,0); + + dest=(struct image*)(o->storage); + if (!getrgb(dest,1,args,"image->rotate()")) + (dest)->rgb=THIS->rgb; + d0.rgb=dest2.rgb=dest->rgb; + + img_skewy(&dest2,dest,-tan(angle/2)*dest2.xsize,xpn); + img_skewx(dest,&d0,sin(angle)*dest->ysize,xpn); + img_skewy(&d0,dest,-tan(angle/2)*d0.xsize,xpn); + + if (dest2.img!=THIS->img) free(dest2.img); + free(d0.img); + + pop_n_elems(args); + push_object(o); +} + +void image_rotate(INT32 args) +{ + img_rotate(args,0); +} + +void image_rotate_expand(INT32 args) +{ + img_rotate(args,1); +} + +void img_translate(INT32 args,int expand) +{ + float xt,yt; + int y,x; + struct object *o; + struct image *img; + rgb_group *s,*d; + + if (args<2) error("illegal number of arguments to image->translate()\n"); + + if (sp[-args].type==T_FLOAT) xt=sp[-args].u.float_number; + else if (sp[-args].type==T_INT) xt=sp[-args].u.integer; + else error("illegal argument 1 to image->translate()\n"); + + if (sp[1-args].type==T_FLOAT) yt=sp[1-args].u.float_number; + else if (sp[1-args].type==T_INT) yt=sp[1-args].u.integer; + else error("illegal argument 2 to image->translate()\n"); + + getrgb(THIS,2,args,"image->translate()\n"); + + xt-=floor(xt); + yt-=floor(yt); + + o=clone(image_program,0); + img=(struct image*)o->storage; + + img->xsize=THIS->xsize+(xt!=0); + img->ysize=THIS->ysize+(xt!=0); + + if (!(img->img=malloc(sizeof(rgb_group)*img->xsize*img->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + if (!xt) + { + memcpy(img->img,THIS->img,sizeof(rgb_group)*THIS->xsize*THIS->ysize); + } + else + { + float xn=1-xt; + + d=img->img; + s=THIS->img; + + for (y=0; y<img->ysize; y++) + { + x=THIS->xsize-1; + if (!expand) + d->r=ROUND(THIS->rgb.r*xt+s->r*xn), + d->g=ROUND(THIS->rgb.g*xt+s->g*xn), + d->b=ROUND(THIS->rgb.b*xt+s->b*xn); + else + d->r=s->r, d->g=s->g, d->b=s->b; + d++; s++; + while (x--) + { + d->r=ROUND(s->r*xn+s[1].r*xt), + d->g=ROUND(s->g*xn+s[1].g*xt), + d->b=ROUND(s->b*xn+s[1].b*xt); + d++; s++; + } + if (!expand) + d->r=ROUND(s->r*xn+THIS->rgb.r*xt), + d->g=ROUND(s->g*xn+THIS->rgb.g*xt), + d->b=ROUND(s->b*xn+THIS->rgb.b*xt); + else + d->r=s->r, d->g=s->g, d->b=s->b; + d++; + } + } + + if (yt) + { + float yn=1-yt; + int xsz=img->xsize; + + d=s=img->img; + + for (x=0; x<img->xsize; x++) + { + y=THIS->ysize-1; + if (!expand) + d->r=ROUND(THIS->rgb.r*yt+s->r*yn), + d->g=ROUND(THIS->rgb.g*yt+s->g*yn), + d->b=ROUND(THIS->rgb.b*yt+s->b*yn); + else + d->r=s->r, d->g=s->g, d->b=s->b; + d+=xsz; s+=xsz; + while (y--) + { + d->r=ROUND(s->r*yn+s[xsz].r*yt), + d->g=ROUND(s->g*yn+s[xsz].g*yt), + d->b=ROUND(s->b*yn+s[xsz].b*yt); + d+=xsz; s+=xsz; + } + if (!expand) + d->r=ROUND(s->r*yn+THIS->rgb.r*yt), + d->g=ROUND(s->g*yn+THIS->rgb.g*yt), + d->b=ROUND(s->b*yn+THIS->rgb.b*yt); + else + d->r=s->r, d->g=s->g, d->b=s->b; + d-=xsz*(img->ysize-1)-1; + s-=xsz*THIS->ysize-1; + } + } + + pop_n_elems(args); + push_object(o); +} + + +void image_translate_expand(INT32 args) +{ + img_translate(args,1); +} + +void image_translate(INT32 args) +{ + img_translate(args,0); +} diff --git a/src/modules/Image/operator.c b/src/modules/Image/operator.c new file mode 100644 index 0000000000000000000000000000000000000000..d411eb6474c15c75c55b2f5ccebd38d0100e1abf --- /dev/null +++ b/src/modules/Image/operator.c @@ -0,0 +1,186 @@ +/* $Id: operator.c,v 1.1 1997/02/11 08:35:45 hubbe Exp $ */ +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" +#include "threads.h" + +#include "image.h" + +extern struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define absdiff(a,b) ((a)<(b)?((b)-(a)):((a)-(b))) +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) + +#define STANDARD_OPERATOR_HEADER(what) \ + struct object *o; \ + struct image *img,*oper; \ + rgb_group *s1,*s2,*d,rgb; \ + INT32 i; \ + \ + if (!THIS->img) error("no image\n"); \ + \ + if (args && sp[-args].type==T_ARRAY \ + && sp[-args].u.array->size>=3 \ + && sp[-args].u.array->item[0].type==T_INT \ + && sp[-args].u.array->item[1].type==T_INT \ + && sp[-args].u.array->item[2].type==T_INT) \ + { \ + rgb.r=sp[-args].u.array->item[0].u.integer; \ + rgb.g=sp[-args].u.array->item[1].u.integer; \ + rgb.b=sp[-args].u.array->item[2].u.integer; \ + oper=NULL; \ + } \ + else \ + { \ + if (args<1 || sp[-args].type!=T_OBJECT \ + || !sp[-args].u.object \ + || sp[-args].u.object->prog!=image_program) \ + error("illegal arguments to image->"what"()\n"); \ + \ + oper=(struct image*)sp[-args].u.object->storage; \ + if (!oper->img) error("no image (operand)\n"); \ + if (oper->xsize!=THIS->xsize \ + || oper->ysize!=THIS->ysize) \ + error("operands differ in size (image->"what")"); \ + } \ + \ + push_int(THIS->xsize); \ + push_int(THIS->ysize); \ + o=clone(image_program,2); \ + img=(struct image*)o->storage; \ + if (!img->img) { free_object(o); error("out of memory\n"); } \ + \ + pop_n_elems(args); \ + push_object(o); \ + \ + s1=THIS->img; \ + if (oper) s2=oper->img; else s2=NULL; \ + d=img->img; \ + \ + i=img->xsize*img->ysize; \ + THREADS_ALLOW(); \ + if (oper) + + +void image_operator_minus(INT32 args) +{ +STANDARD_OPERATOR_HEADER("'-") + while (i--) + { + d->r=absdiff(s1->r,s2->r); + d->g=absdiff(s1->g,s2->g); + d->b=absdiff(s1->b,s2->b); + s1++; s2++; d++; + } + else + while (i--) + { + d->r=absdiff(s1->r,rgb.r); + d->g=absdiff(s1->g,rgb.g); + d->b=absdiff(s1->b,rgb.b); + s1++; d++; + } + THREADS_DISALLOW(); +} + +void image_operator_plus(INT32 args) +{ +STANDARD_OPERATOR_HEADER("'+") + while (i--) + { + d->r=max(s1->r+s2->r,255); + d->g=max(s1->g+s2->g,255); + d->b=max(s1->b+s2->b,255); + s1++; s2++; d++; + } + else + while (i--) + { + d->r=max(s1->r+rgb.r,255); + d->g=max(s1->g+rgb.g,255); + d->b=max(s1->b+rgb.b,255); + s1++; d++; + } + THREADS_DISALLOW(); +} + +void image_operator_multiply(INT32 args) +{ + double q=1/255.0; +STANDARD_OPERATOR_HEADER("'+") + while (i--) + { + d->r=floor(s1->r*s2->r*q+0.5); + d->g=floor(s1->g*s2->g*q+0.5); + d->b=floor(s1->b*s2->b*q+0.5); + s1++; s2++; d++; + } + else + while (i--) + { + d->r=floor(s1->r*rgb.r*q+0.5); + d->g=floor(s1->g*rgb.g*q+0.5); + d->b=floor(s1->b*rgb.b*q+0.5); + s1++; d++; + } + THREADS_DISALLOW(); +} + +void image_operator_maximum(INT32 args) +{ +STANDARD_OPERATOR_HEADER("'| 'maximum'") + while (i--) + { + d->r=max(s1->r,s2->r); + d->g=max(s1->g,s2->g); + d->b=max(s1->b,s2->b); + s1++; s2++; d++; + } + else + while (i--) + { + d->r=max(s1->r,rgb.r); + d->g=max(s1->g,rgb.g); + d->b=max(s1->b,rgb.b); + s1++; s2++; d++; + } + THREADS_DISALLOW(); +} + +void image_operator_minimum(INT32 args) +{ +STANDARD_OPERATOR_HEADER("'& 'minimum'") + while (i--) + { + d->r=min(s1->r,s2->r); + d->g=min(s1->g,s2->g); + d->b=min(s1->b,s2->b); + s1++; s2++; d++; + } + else + while (i--) + { + d->r=min(s1->r,rgb.r); + d->g=min(s1->g,rgb.g); + d->b=min(s1->b,rgb.b); + s1++; d++; + } + THREADS_DISALLOW(); +} + + diff --git a/src/modules/Image/pattern.c b/src/modules/Image/pattern.c new file mode 100644 index 0000000000000000000000000000000000000000..f96e2dd5fe5a7568ea7000b24ddb4642d61cc581 --- /dev/null +++ b/src/modules/Image/pattern.c @@ -0,0 +1,309 @@ +/* $Id: pattern.c,v 1.1 1997/02/11 08:35:45 hubbe Exp $ */ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" + +#include "image.h" + +extern struct program *image_program; +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) +#define testrange(x) max(min((x),255),0) + +/**************** noise ************************/ + +#define NOISE_PTS 512 +#define NOISE_PX 173 +#define NOISE_PY 263 +#define NOISE_PZ 337 +#define NOISE_PHI 0.6180339 +static unsigned short noise_p1[NOISE_PTS],noise_p2[NOISE_PTS]; + +#define COLORRANGE_LEVELS 1024 + +#define FRAC(X) ((X)-floor(X)) + +static INLINE double noise(double Vx,double Vy,unsigned short *noise_p) +{ + int Ax[3],Ay[3]; + int n,i,j; + double Sx[3],Sy[3]; + double sum,dsum,f,fx,fy; + + fx=floor(Vx); + fy=floor(Vy); + + for (n=0; n<3; n++) + { + Ax[n]=(int)floor(NOISE_PX*FRAC( (fx+n)*NOISE_PHI )); + Ay[n]=(int)floor(NOISE_PY*FRAC( (fy+n)*NOISE_PHI )); + } + + f=FRAC(Vx); + Sx[0]=0.5-f+0.5*f*f; + Sx[1]=0.5+f-f*f; + Sx[2]=0.5*f*f; + + f=FRAC(Vy); + Sy[0]=0.5-f+0.5*f*f; + Sy[1]=0.5+f-f*f; + Sy[2]=0.5*f*f; + + sum=0; + for (i=0; i<3; i++) + { + for (j=0,dsum=0; j<3; j++) + dsum+=Sy[j]*noise_p[ (Ax[i]+Ay[j]) & (NOISE_PTS-1) ]; + sum+=Sx[i]*dsum; + } + return sum; +} + +static INLINE double turbulence(double x,double y,int octaves) +{ + double t=0; + double mul=1; + while (octaves-->0) + { + t+=noise(x*mul,y*mul,noise_p1)*mul; + mul*=0.5; + } + return t; +} + +static void init_colorrange(rgb_group *cr,struct svalue *s,char *where) +{ + float *v,*vp; + int i,n,k; + struct svalue s2,s3; + rgbd_group lrgb,*rgbp,*rgb; + float fr,fg,fb,q; + int b; + + if (s->type!=T_ARRAY) + error("Illegal colorrange to %s\n",where); + else if (s->u.array->size<2) + error("Colorrange array too small (meaningless) (to %s)\n",where); + + s2.type=T_INT; + s3.type=T_INT; /* don't free these */ + + vp=v=(void*)xalloc(sizeof(float)*(s->u.array->size/2+1)); + rgbp=rgb=(void*)xalloc(sizeof(rgbd_group)*(s->u.array->size/2+1)); + + for (i=0; i<s->u.array->size-1; i+=2) + { + array_index(&s2,s->u.array,i); + if (s2.type==T_INT) *vp=s2.u.integer; + else if (s2.type==T_FLOAT) *vp=s2.u.float_number; + else *vp=0; + if (*vp>1) *vp=1; + else if (*vp<0) *vp=0; + vp++; + + array_index(&s2,s->u.array,i+1); + + if (s2.type==T_INT) + rgbp->r=rgbp->g=rgbp->b=testrange( s2.u.integer ); + else if ( s2.type==T_ARRAY + && s2.u.array->size>=3 ) + { + array_index(&s3,s2.u.array,0); + if (s3.type==T_INT) rgbp->r=testrange( s3.u.integer ); + else rgbp->r=0; + array_index(&s3,s2.u.array,1); + if (s3.type==T_INT) rgbp->g=testrange( s3.u.integer ); + else rgbp->g=0; + array_index(&s3,s2.u.array,2); + if (s3.type==T_INT) rgbp->b=testrange( s3.u.integer ); + else rgbp->b=0; + } + else + rgbp->r=rgbp->g=rgbp->b=0; + rgbp++; + } + *vp=v[0]+1+1.0/(COLORRANGE_LEVELS-1); + lrgb=*rgbp=rgb[0]; /* back to original color */ + + for (k=1,i=v[0]*(COLORRANGE_LEVELS-1); k<=s->u.array->size/2; k++) + { + n=v[k]*(COLORRANGE_LEVELS-1); + + if (n>i) + { + q=1/((float)(n-i)); + + fr=(rgb[k].r-lrgb.r)*q; + fg=(rgb[k].g-lrgb.g)*q; + fb=(rgb[k].b-lrgb.b)*q; + + for (b=0;i<n;i++,b++) + { + cr[i&(COLORRANGE_LEVELS-1)].r=(unsigned char)(lrgb.r+fr*b); + cr[i&(COLORRANGE_LEVELS-1)].g=(unsigned char)(lrgb.g+fg*b); + cr[i&(COLORRANGE_LEVELS-1)].b=(unsigned char)(lrgb.b+fb*b); + } + } + lrgb=rgb[k]; + } + + free_svalue(&s3); + free_svalue(&s2); + free(v); + free(rgb); +} + +#define GET_FLOAT_ARG(sp,args,n,def,where) \ + ( (args>n) \ + ? ( (sp[n-args].type==T_INT) ? (double)(sp[n-args].u.integer) \ + : ( (sp[n-args].type==T_FLOAT) ? sp[n-args].u.float_number \ + : ( error("illegal argument(s) to "where"\n"), 0.0 ) ) ) \ + : def ) +#define GET_INT_ARG(sp,args,n,def,where) \ + ( (args>n) \ + ? ( (sp[n-args].type==T_INT) ? sp[n-args].u.integer \ + : ( (sp[n-args].type==T_FLOAT) ? (int)(sp[n-args].u.float_number) \ + : ( error("illegal argument(s) to "where"\n"), 0.0 ) ) ) \ + : def ) + +void image_noise(INT32 args) +{ +/* parametrar: array(float|int|array(int)) colorrange, + float scale=0.1, + float xdiff=0, + float ydiff=0, + float cscale=1 +*/ + int x,y; + rgb_group cr[COLORRANGE_LEVELS]; + double scale,xdiff,ydiff,cscale,xp,yp; + rgb_group *d; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + + if (args<1) error("too few arguments to image->noise()\n"); + + scale=GET_FLOAT_ARG(sp,args,1,0.1,"image->noise"); + xdiff=GET_FLOAT_ARG(sp,args,2,0,"image->noise"); + ydiff=GET_FLOAT_ARG(sp,args,3,0,"image->noise"); + cscale=GET_FLOAT_ARG(sp,args,4,1,"image->noise"); + + init_colorrange(cr,sp-args,"image->noise()"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + cscale=(32768*cscale)/COLORRANGE_LEVELS; + + d=img->img; + for (y=THIS->ysize,xp=xdiff; y--; xp+=1.0) + for (x=THIS->xsize,yp=ydiff; x--; yp+=1.0) + { + *(d++)= + cr[(int)((noise((double)x*scale,(double)y*scale,noise_p1) + +noise((double)(x*0.5+y*0.8660254037844386)*scale, + (double)(-y*0.5+x*0.8660254037844386)*scale, + noise_p2)) + *cscale)&(COLORRANGE_LEVELS-1)]; + } + + pop_n_elems(args); + push_object(o); +} + +void image_turbulence(INT32 args) +{ +/* parametrar: array(float|int|array(int)) colorrange, + int octaves=3, + float scale=0.1, + float xdiff=0, + float ydiff=0, + float cscale=0.001 +*/ + int x,y,octaves; + rgb_group cr[COLORRANGE_LEVELS]; + double scale,xdiff,ydiff,cscale,xp,yp; + rgb_group *d; + struct object *o; + struct image *img; + + if (!THIS->img) error("no image\n"); + + if (args<1) error("too few arguments to image->turbulence()\n"); + + octaves=GET_INT_ARG(sp,args,1,3,"image->turbulence"); + scale=GET_FLOAT_ARG(sp,args,2,0.1,"image->turbulence"); + xdiff=GET_FLOAT_ARG(sp,args,3,0,"image->turbulence"); + ydiff=GET_FLOAT_ARG(sp,args,4,0,"image->turbulence"); + cscale=GET_FLOAT_ARG(sp,args,5,0.001,"image->turbulence"); + + init_colorrange(cr,sp-args,"image->turbulence()"); + + o=clone(image_program,0); + img=(struct image*)o->storage; + *img=*THIS; + if (!(img->img=malloc(sizeof(rgb_group)*THIS->xsize*THIS->ysize+1))) + { + free_object(o); + error("Out of memory\n"); + } + + cscale=(32768*cscale)/COLORRANGE_LEVELS; + + d=img->img; + for (y=THIS->ysize,xp=xdiff; y--; xp+=1.0) + for (x=THIS->xsize,yp=ydiff; x--; yp+=1.0) + { +#if 0 + if (y==0 && x<10) + { + fprintf(stderr,"%g*%g=%d => %d\n", + turbulence(xp*scale,yp*scale,octaves), + cscale, + (INT32)(turbulence(xp*scale,yp*scale,octaves)*cscale), + (INT32)(turbulence(xp*scale,yp*scale,octaves)*cscale)&(COLORRANGE_LEVELS)-1 ); + } +#endif + *(d++)= + cr[(INT32)(turbulence(xp*scale,yp*scale,octaves)*cscale) + & (COLORRANGE_LEVELS-1)]; + } + + pop_n_elems(args); + push_object(o); +} + + +void image_noise_init(void) +{ + int n; + for (n=0; n<NOISE_PTS; n++) + { + noise_p1[n]=(unsigned short)(rand()&32767); + noise_p2[n]=(unsigned short)(rand()&32767); + } +} diff --git a/src/modules/Image/pnm.c b/src/modules/Image/pnm.c new file mode 100644 index 0000000000000000000000000000000000000000..478047a8f60737a183121bf6e5f933b192067224 --- /dev/null +++ b/src/modules/Image/pnm.c @@ -0,0 +1,172 @@ +/* $Id: pnm.c,v 1.1 1997/02/11 08:35:45 hubbe Exp $ */ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" + +#include "image.h" + +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) +#define pixel(_img,x,y) ((_img)->img[(x)+(y)*(_img)->xsize]) + + +static INLINE unsigned char getnext(struct pike_string *s,INT32 *pos) +{ + if (*pos>=s->len) return 0; + if (s->str[(*pos)]=='#') + for (;*pos<s->len && ISSPACE(s->str[*pos]);(*pos)++); + return s->str[(*pos)++]; +} + +static INLINE void skip_to_eol(struct pike_string *s,INT32 *pos) +{ + for (;*pos<s->len && s->str[*pos]!=10;(*pos)++); +} + +static INLINE unsigned char getnext_skip_comment(struct pike_string *s,INT32 *pos) +{ + unsigned char c; + while ((c=getnext(s,pos))=='#') + skip_to_eol(s,pos); + return c; +} + +static INLINE void skipwhite(struct pike_string *s,INT32 *pos) +{ + while (*pos<s->len && + ( ISSPACE(s->str[*pos]) || + s->str[*pos]=='#')) + getnext_skip_comment(s,pos); +} + +static INLINE INT32 getnextnum(struct pike_string *s,INT32 *pos) +{ + INT32 i; + skipwhite(s,pos); + i=0; + while (*pos<s->len && + s->str[*pos]>='0' && s->str[*pos]<='9') + { + i=(i*10)+s->str[*pos]-'0'; + getnext(s,pos); + } + return i; +} + +static char* img_frompnm(struct pike_string *s) +{ + struct image new; + INT32 type,c=0,maxval=255; + INT32 pos=0,x,y,i; + + skipwhite(s,&pos); + if (getnext(s,&pos)!='P') return "not pnm"; /* not pnm */ + type=getnext(s,&pos); + if (type<'1'||type>'6') return "unknown type"; /* unknown type */ + new.xsize=getnextnum(s,&pos); + new.ysize=getnextnum(s,&pos); + if (new.xsize<=0||new.ysize<=0) return "illegal size"; /* illegal size */ + if (type=='3'||type=='2'||type=='6'||type=='5') + maxval=getnextnum(s,&pos); + new.img=malloc(new.xsize*new.ysize*sizeof(rgb_group)+1); + if (!new.img) error("Out of memory.\n"); + + if (type=='1'||type=='2'||type=='3') + { + skipwhite(s,&pos); + } + else + { + skip_to_eol(s,&pos); + pos++; + } + for (y=0; y<new.ysize; y++) + { + for (i=0,x=0; x<new.xsize; x++) + { + switch (type) + { + case '1': + c=getnextnum(s,&pos); + pixel(&new,x,y).r=pixel(&new,x,y).g=pixel(&new,x,y).b= + (unsigned char)~(c*255); + break; + case '2': + c=getnextnum(s,&pos); + pixel(&new,x,y).r=pixel(&new,x,y).g=pixel(&new,x,y).b= + (unsigned char)((c*255L)/maxval); + break; + case '3': + pixel(&new,x,y).r=(unsigned char)((getnextnum(s,&pos)*255L)/maxval); + pixel(&new,x,y).g=(unsigned char)((getnextnum(s,&pos)*255L)/maxval); + pixel(&new,x,y).b=(unsigned char)((getnextnum(s,&pos)*255L)/maxval); + break; + case '4': + if (!i) c=getnext(s,&pos),i=8; + pixel(&new,x,y).r=pixel(&new,x,y).g=pixel(&new,x,y).b= + (unsigned char)~(((c>>7)&1)*255); + c<<=1; + i--; + break; + case '5': + c=getnext(s,&pos); + pixel(&new,x,y).r=pixel(&new,x,y).g=pixel(&new,x,y).b= + (unsigned char)((c*255L)/maxval); + break; + case '6': + pixel(&new,x,y).r=(unsigned char)((getnext(s,&pos)*255L)/maxval); + pixel(&new,x,y).g=(unsigned char)((getnext(s,&pos)*255L)/maxval); + pixel(&new,x,y).b=(unsigned char)((getnext(s,&pos)*255L)/maxval); + break; + } + } + } + if (THIS->img) free(THIS->img); + THIS->xsize=new.xsize; + THIS->ysize=new.ysize; + THIS->img=new.img; + return NULL; +} + +void image_toppm(INT32 args) +{ + char buf[80]; + struct pike_string *a,*b; + + pop_n_elems(args); + if (!THIS->img) { error("no image\n"); return; } + sprintf(buf,"P6\n%d %d\n255\n",THIS->xsize,THIS->ysize); + a=make_shared_string(buf); + b=make_shared_binary_string((char*)THIS->img, + THIS->xsize*THIS->ysize*3); + push_string(add_shared_strings(a,b)); + free_string(a); + free_string(b); +} + + +void image_frompnm(INT32 args) +{ + char *s; + if (args<1|| + sp[-args].type!=T_STRING) + error("Illegal argument to image->frompnm()\n"); + s=img_frompnm(sp[-args].u.string); + pop_n_elems(args); + if (!s) { push_object(THISOBJ); THISOBJ->refs++; } + else push_string(make_shared_string(s)); +} + diff --git a/src/modules/Image/quant.c b/src/modules/Image/quant.c new file mode 100644 index 0000000000000000000000000000000000000000..60c3e08a304e1a054cda54a4372c7a7ea0cc7a44 --- /dev/null +++ b/src/modules/Image/quant.c @@ -0,0 +1,924 @@ +#include <config.h> +/* $Id: quant.c,v 1.1 1997/02/11 08:35:46 hubbe Exp $ */ + +/* + +quant, used by image when making gif's (mainly) + +Pontus Hagland, law@infovav.se +David K�gedal, kg@infovav.se + +*/ + +#include <unistd.h> +#include <stdio.h> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#include "types.h" +#include "error.h" +#include "global.h" +#include "array.h" + +#include "threads.h" + + +#include "image.h" + +/* +#define QUANT_DEBUG_GAP +#define QUANT_DEBUG_POINT +#define QUANT_DEBUG +#define QUANT_DEBUG_RGB +*/ + +#define QUANT_MAXIMUM_NUMBER_OF_COLORS 65535 + +/**********************************************************************/ + +#if 0 +#include <sys/resource.h> +#define CHRONO(X) chrono(X) + +static void chrono(char *x) +{ + struct rusage r; + static struct rusage rold; + getrusage(RUSAGE_SELF,&r); + fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x, + r.ru_utime.tv_sec,r.ru_utime.tv_usec, + + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0) + +r.ru_utime.tv_sec-rold.ru_utime.tv_sec, + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0) + + r.ru_utime.tv_usec-rold.ru_utime.tv_usec + ); + + rold=r; +} +#else +#define CHRONO(X) +#endif + +/**********************************************************************/ + +#define sq(x) ((x)*(x)) +#define DISTANCE(A,B) \ + (sq(((int)(A).r)-((int)(B).r)) \ + +sq(((int)(A).g)-((int)(B).g)) \ + +sq(((int)(A).b)-((int)(B).b))) + + +typedef struct +{ + rgb_group rgb; + unsigned long count; + unsigned long next; +} rgb_entry; + +typedef struct +{ + unsigned long len; + rgb_entry tbl[1]; +} rgb_hashtbl; + +#define hash(rgb,l) (((rgb).r*127+(rgb).g*997+(rgb).b*2111)&(l-1)) +#define same_col(rgb1,rgb2) \ + (((rgb1).r==(rgb2).r) && ((rgb1).g==(rgb2).g) && ((rgb1).b==(rgb2).b)) + + +static INLINE int hash_enter(rgb_entry *rgbe,rgb_entry *end, + int len,rgb_group rgb) +{ + register rgb_entry *re; + + re=rgbe+hash(rgb,len); +/* fprintf(stderr,"%d\n",hash(rgb,len));*/ + + while (re->count && !same_col(re->rgb,rgb)) + if (++re==end) re=rgbe; + + if (!re->count) /* allocate it */ + { + re->rgb=rgb; + re->count=1; + return 1; + } + + re->count++; + return 0; +} + +static INLINE int hash_enter_strip(rgb_entry *rgbe,rgb_entry *end, + int len,rgb_group rgb,int strip) +{ + register rgb_entry *re; + + unsigned char strip_r[24]= +{ 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, + 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x80 }; + unsigned char strip_g[24]= +{ 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, + 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x80 }; + unsigned char strip_b[24]= +{ 0xfe, 0xfe, 0xfe, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, + 0xf0, 0xf0, 0xf0, 0xe0, 0xe0, 0xe0, 0xc0, 0xc0, 0xc0, 0x80, 0x80, 0x80 }; + + rgb.r&=strip_r[strip]; + rgb.g&=strip_g[strip]; + rgb.b&=strip_b[strip]; + + re=rgbe+hash(rgb,len); + + while (re->count && !same_col(re->rgb,rgb)) + if (++re==end) re=rgbe; + + if (!re->count) /* allocate it */ + { + re->rgb=rgb; + re->count=1; + return 1; + } + + re->count++; + return 0; +} + +static rgb_hashtbl *img_rehash(rgb_hashtbl *old, unsigned long newsize) +{ + unsigned long i; + rgb_hashtbl *new = malloc(sizeof(rgb_hashtbl) + + sizeof(rgb_entry) * newsize ); + MEMSET(new->tbl, 0, sizeof(rgb_entry) * newsize ); + + if (!new) error("Out of memory\n"); +#ifdef QUANT_DEBUG + fprintf(stderr,"img_rehash: old size = %lu, new size = %lu\n", + (old ? old->len : 0), newsize); +#endif + + new->len = newsize; + if (old) + { + for (i=0;i<old->len;i++) + if (old->tbl[i].count) + { + register rgb_entry *re; + + re=new->tbl+hash(old->tbl[i].rgb,newsize); + + while (re->count) + if (++re==new->tbl+newsize) re=new->tbl; + + *re=old->tbl[i]; + } + free(old); + } + return new; +} + +static int cmp_red(rgb_entry *e1, rgb_entry *e2) +{ + return e1->rgb.r - e2->rgb.r; +} + +static int cmp_green(rgb_entry *e1, rgb_entry *e2) +{ + return e1->rgb.g - e2->rgb.g; +} + +static int cmp_blue(rgb_entry *e1, rgb_entry *e2) +{ + return e1->rgb.b - e2->rgb.b; +} + +static int get_tbl_median(rgb_entry *tbl,int len) +{ + int a=0,x=0; + rgb_entry *m,*n; + + len--; + m=tbl; n=tbl+len-1; + while (m<n) + { +#if 0 + fprintf(stderr,"pos %3d,%3d sum %3d low=%3d,%3d,%3dc%3d high=%3d,%3d,%3dc%3d\n", + x,len,a, + tbl[x].rgb.r,tbl[x].rgb.g,tbl[x].rgb.b,tbl[x].count, + tbl[len].rgb.r,tbl[len].rgb.g,tbl[len].rgb.b,tbl[len].count); +#endif + if (a>0) a-=m++->count; + else a+=n--->count; + } + return m-tbl; +} + +static rgb_group get_tbl_point(rgb_entry *tbl,int len) +{ + unsigned long r=0,g=0,b=0,n=0; + int x; + rgb_group rgb; + for (x=0; x<len; x++) + { + r+=((unsigned long)tbl[x].rgb.r)*tbl[x].count, + g+=((unsigned long)tbl[x].rgb.g)*tbl[x].count, + b+=((unsigned long)tbl[x].rgb.b)*tbl[x].count; + n+=tbl[x].count; +#ifdef QUANT_DEBUG_POINT + fprintf(stderr,"(%d,%d,%d)*%lu; ", + tbl[x].rgb.r, + tbl[x].rgb.g, + tbl[x].rgb.b, + tbl[x].count); +#endif + } + rgb.r=(unsigned char)(r/n); + rgb.g=(unsigned char)(g/n); + rgb.b=(unsigned char)(b/n); +#ifdef QUANT_DEBUG_POINT + fprintf(stderr,"-> (%lu,%lu,%lu)/%lu = %d,%d,%d\n", + r,g,b,n, + rgb.r,rgb.g,rgb.b); +#endif + return rgb; +} + +#define MAKE_BINSORT(C,NAME) \ + void NAME(rgb_entry *e, unsigned long len) \ + { \ + unsigned long pos[256];\ + unsigned long i,j,k;\ + \ + rgb_entry *e2;\ + e2=(rgb_entry*)xalloc(sizeof(rgb_entry)*len);\ + \ + for (i=0; i<256; i++) pos[i]=~0;\ + \ + for (i=0; i<len; i++)\ + {\ + e[i].next=pos[j=e[i].rgb.C];\ + pos[j]=i;\ + }\ + \ + for (i=j=0; i<256; i++)\ + {\ + k=pos[i];\ + while (k!=(unsigned long)~0)\ + {\ + e2[j++]=e[k];\ + k=e[k].next;\ + }\ + }\ + MEMCPY(e,e2,len*sizeof(rgb_entry));\ + free(e2);\ + } + +MAKE_BINSORT(r,binsort_red) +MAKE_BINSORT(g,binsort_green) +MAKE_BINSORT(b,binsort_blue) + + +static void sort_tbl(rgb_hashtbl *ht, + unsigned long start, unsigned long len, + int level, unsigned long idx, + unsigned long gap, int ldir, + struct colortable *ct, rgb_group lower,rgb_group upper, + unsigned long **rn_next, + unsigned long *rgb_node) +{ + rgb_entry *tbl = ht->tbl; + + int (*sortfun)(const void *, const void *); + unsigned long x,y; + int dir=0; + +#ifdef QUANT_DEBUG + + fprintf(stderr,"%*ssort_tbl: level %d start = %lu " + " len=%lu, idx = %lu, gap = %lu", + level, "", + level, start, len, idx, gap); + fprintf(stderr,"\n%*s%d,%d,%d-%d,%d,%d ",level,"", + lower.r,lower.g,lower.b,upper.r,upper.g,upper.b); + +#endif + + if (len>1) + { + /* check which direction has the most span */ + /* we make it easy for us, only three directions: r,g,b */ + + +#define PRIO_RED 2 +#define PRIO_GREEN 4 +#define PRIO_BLUE 1 + +#if 0 + rgb_group min,max; + + max.r=min.r=tbl[start].rgb.r; + max.g=min.g=tbl[start].rgb.g; + max.b=min.b=tbl[start].rgb.b; + + for (x=1; x<len; x++) + { + if (min.r>tbl[start+x].rgb.r) min.r=tbl[start+x].rgb.r; + if (min.g>tbl[start+x].rgb.g) min.g=tbl[start+x].rgb.g; + if (min.b>tbl[start+x].rgb.b) min.b=tbl[start+x].rgb.b; + if (max.r<tbl[start+x].rgb.r) max.r=tbl[start+x].rgb.r; + if (max.g<tbl[start+x].rgb.g) max.g=tbl[start+x].rgb.g; + if (max.b<tbl[start+x].rgb.b) max.b=tbl[start+x].rgb.b; + } +#ifdef QUANT_DEBUG +fprintf(stderr,"space: %d,%d,%d-%d,%d,%d ", + min.r,min.g,min.b, + max.r,max.g,max.b); +#endif + + /* do this weighted, red=2 green=3 blue=1 */ + if ((max.r-min.r)*PRIO_RED>(max.g-min.g)*PRIO_GREEN) /* r>g */ + if ((max.r-min.r)*PRIO_RED>(max.b-min.b)*PRIO_BLUE) /* r>g, r>b */ + dir=0; + else /* r>g, b>r */ + dir=2; + else + if ((max.g-min.g)*PRIO_GREEN>(max.b-min.b)*PRIO_BLUE) /* g>r, g>b */ + dir=1; + else /* g>r, b>g */ + dir=2; +#endif + + rgbl_group sum={0,0,0}; + rgb_group mid; + + for (x=0; x<len; x++) + { + sum.r+=tbl[start+x].rgb.r; + sum.g+=tbl[start+x].rgb.g; + sum.b+=tbl[start+x].rgb.b; + } + mid.r=(unsigned char)(sum.r/len); + mid.g=(unsigned char)(sum.g/len); + mid.b=(unsigned char)(sum.b/len); + + sum.r=sum.g=sum.b=0; + for (x=0; x<len; x++) + { + sum.r+=sq(tbl[start+x].rgb.r-mid.r); + sum.g+=sq(tbl[start+x].rgb.g-mid.g); + sum.b+=sq(tbl[start+x].rgb.b-mid.b); + } + + if (sum.r*PRIO_RED>sum.g*PRIO_GREEN) /* r>g */ + if (sum.r*PRIO_RED>sum.b*PRIO_BLUE) /* r>g, r>b */ + dir=0; + else /* r>g, b>r */ + dir=2; + else + if (sum.g*PRIO_GREEN>sum.b*PRIO_BLUE) /* g>r, g>b */ + dir=1; + else /* g>r, b>g */ + dir=2; + + +#ifdef QUANT_DEBUG + fprintf(stderr," dir=%d ",dir); +#endif + if (dir!=ldir) + switch (dir) + { + case 0: binsort_red(tbl+start,len); break; + case 1: binsort_green(tbl+start,len); break; + case 2: binsort_blue(tbl+start,len); break; + } + +#ifdef QUANT_DEBUG + fprintf(stderr,"low: %d,%d,%d high: %d,%d,%d\n", + tbl[start].rgb.r, + tbl[start].rgb.g, + tbl[start].rgb.b, + tbl[start+len-1].rgb.r, + tbl[start+len-1].rgb.g, + tbl[start+len-1].rgb.b); +#endif + + } + + if (len>1 && gap>1) /* recurse */ + { + unsigned long pos,g1,g2; + rgb_group less,more,rgb; + unsigned char split_on=0; + int i; + + pos=get_tbl_median(tbl+start,len); + + rgb=tbl[start+pos].rgb; + + less=upper; + more=lower; + + switch (dir) + { + case 0: more.r=rgb.r+1; split_on=less.r=rgb.r; + while (pos<len-1 && tbl[start].rgb.r==tbl[start+pos].rgb.r) pos++; + while (pos>0 && tbl[start+len-1].rgb.r==tbl[start+pos].rgb.r) pos--; + break; + case 1: more.g=rgb.g+1; split_on=less.g=rgb.g; + while (pos<len-1 && tbl[start].rgb.g==tbl[start+pos].rgb.g) pos++; + while (pos>0 && tbl[start+len-1].rgb.g==tbl[start+pos].rgb.g) pos--; + break; + case 2: more.b=rgb.b+1; split_on=less.b=rgb.b; + while (pos<len-1 && tbl[start].rgb.b==tbl[start+pos].rgb.b) pos++; + while (pos>0 && tbl[start+len-1].rgb.b==tbl[start+pos].rgb.b) pos--; + break; + } + +#ifdef QUANT_DEBUG + fprintf(stderr, " pos=%lu len=%lu\n",pos,len); +#endif + +#ifdef QUANT_DEBUG_GAP + + fprintf(stderr,"\n left: "); + for (i=0; i<=pos; i++) + fprintf(stderr,"%d,%d,%d; ", + tbl[start+i].rgb.r,tbl[start+i].rgb.g,tbl[start+i].rgb.b); + fprintf(stderr,"\n right: "); + for (; i<len; i++) + fprintf(stderr,"%d,%d,%d; ", + tbl[start+i].rgb.r,tbl[start+i].rgb.g,tbl[start+i].rgb.b); + fprintf(stderr,"\n"); + +#endif + g1=gap>>1; + +#ifdef QUANT_DEBUG_GAP + fprintf(stderr,"gap: %d / %d pos+1=%d len-pos-1=%d gap-g1=%d\n", + g1,gap-g1,pos+1,len-pos-1,gap-g1); +#endif + + if (pos+1<g1) g1=pos+1,g2=gap-g1; + else if (len-pos-1<gap-g1) g2=(len-pos-1),g1=gap-g2; + else g2=gap-g1; + +#ifdef QUANT_DEBUG + fprintf(stderr,"gap: %d / %d ",g1,g2); +#endif + + if (gap>1) + { + /* split tree */ + + *rgb_node= + ( (*rn_next)-ct->rgb_node ) + | ( ((unsigned long)split_on) << 22 ) + | ( (dir+1)<<30 ) & 0xffffffff; + rgb_node=*rn_next; + (*rn_next)+=2; + + sort_tbl(ht,start,pos+1, + level+1,idx,g1,dir, + ct,lower,less,rn_next,rgb_node++); + + sort_tbl(ht,start+pos+1,len-pos-1, + level+1,idx+g1,g2,dir, + ct,more,upper,rn_next,rgb_node); + } + else + { + /* this shouldn't occur? /law */ + abort(); + sort_tbl(ht,start,pos+1, + level+1,idx,g1,dir, + ct,lower,less,rn_next,rgb_node); + } + return; + } + else + { + int r,g,b; + if (len>1) + ct->clut[idx]=get_tbl_point(tbl+start,len); + else + ct->clut[idx]=tbl[start].rgb; + +#ifdef QUANT_DEBUG + fprintf(stderr,"-> end node [%d,%d,%d] - [%d,%d,%d] => %d,%d,%d\n", + lower.r, lower.g, lower.b, upper.r, upper.g, upper.b, + ct->clut[idx].r,ct->clut[idx].g,ct->clut[idx].b); +#endif + + /* write end node */ + *rgb_node=idx; /* done */ + } +} + +static struct colortable *colortable_allocate(int numcol) +{ + struct colortable *ct; + ct = malloc(sizeof(struct colortable)+ + sizeof(rgb_group)*numcol); + if (!ct) error("Out of memory.\n"); + MEMSET(ct,0,sizeof(struct colortable)+ + sizeof(rgb_group)*numcol); + ct->numcol=numcol; + ct->rgb_node=malloc(sizeof(unsigned long)*numcol*4); + MEMSET(ct->rgb_node,0, + sizeof(unsigned long)*numcol*4); + return ct; +} + +struct colortable *colortable_quant(struct image *img,int numcol) +{ + rgb_hashtbl *tbl; + INT32 i,j; + INT32 sz = img->xsize * img->ysize; + rgb_entry entry; + struct colortable *ct=NULL; + rgb_group black,white; + rgb_group *p; + INT32 entries=0; + unsigned long *next_free_rgb_node; + + THREADS_ALLOW(); +#ifdef QUANT_DEBUG + fprintf(stderr,"img_quant called\n"); +#endif + CHRONO("quant"); + + if (numcol<2) numcol=2; + + if (numcol>MAX_NUMCOL) numcol=MAX_NUMCOL; + + ct = colortable_allocate(numcol); + +#ifdef QUANT_DEBUG + fprintf(stderr,"Moving colors into hashtable\n"); +#endif + + for (;;) + { + int strip=0; +rerun: + entries=0; + + + CHRONO("hash init"); + + tbl = img_rehash(NULL, 8192); + + p=img->img; + i=sz; + do + { + register rgb_entry *rgbe,*end; + int len,trig; + +#ifdef QUANT_DEBUG + fprintf(stderr,"hash: %d pixels left...\n",i); +#endif + CHRONO("hash..."); + + len=tbl->len; + trig=(len*2)/10; /* 20% full => rehash bigger */ + end=(rgbe=tbl->tbl)+tbl->len; + + if (!strip) + { + while (i--) + if ( (entries+=hash_enter(rgbe,end,len,*(p++))) > trig ) + { +#ifdef QUANT_DEBUG + fprintf(stderr,"rehash: 20%% = %d / %d...\n",entries,len); +#endif + CHRONO("rehash..."); + if ((len<<2) > QUANT_MAXIMUM_NUMBER_OF_COLORS) + { + strip++; + fprintf(stderr,"strip: %d\n",strip); + free(tbl); + goto rerun; + } + tbl=img_rehash(tbl,len<<2); /* multiple size by 4 */ + break; + } + } + else + while (i--) + if ( (entries+=hash_enter_strip(rgbe,end,len,*(p++),strip)) > trig ) + { +#ifdef QUANT_DEBUG + fprintf(stderr,"rehash: 20%% = %d / %d...\n",entries,len); +#endif + CHRONO("rehash..."); + if ((len<<2) > QUANT_MAXIMUM_NUMBER_OF_COLORS) + { + strip++; + free(tbl); + goto rerun; + } + tbl=img_rehash(tbl,len<<2); /* multiple size by 4 */ + break; + } + } + while (i>=0); + break; + } + + /* Compact the hash table */ + CHRONO("compact"); + +#ifdef QUANT_DEBUG + fprintf(stderr,"Compacting\n"); +#endif + i = tbl->len - 1; + j = 0; + while (i > entries) + { + while ((i >= entries) && tbl->tbl[i].count == 0) i--; + while ((j < entries) && tbl->tbl[j].count != 0) j++; + if (j<i) + { + tbl->tbl[j] = tbl->tbl[i]; + tbl->tbl[i].count = 0; + } + } + + white.r=white.g=white.b=255; + black.r=black.g=black.b=0; + +#ifdef QUANT_DEBUG + fprintf(stderr,"%d colors found, sorting and quantizing...\n",j); +#endif + + CHRONO("sort"); + if (j<numcol) ct->numcol=numcol=j; + + next_free_rgb_node=ct->rgb_node+1; + sort_tbl(tbl, 0, j, 0, 0, numcol, -1, ct, black, white, + &next_free_rgb_node,ct->rgb_node); + +#ifdef QUANT_DEBUG + fprintf(stderr,"img_quant done, %d colors selected\n", numcol); +#endif + CHRONO("done"); + + free(tbl); + CHRONO("really done"); + THREADS_DISALLOW(); + return ct; +} + + +struct colortable *colortable_from_array(struct array *arr,char *from) +{ + rgb_hashtbl *tbl; + INT32 i,j; + struct colortable *ct=NULL; + rgb_group black,white; + rgb_group *p; + INT32 entries=0; + struct svalue s,s2; + unsigned long *next_free_rgb_node; + + THREADS_ALLOW(); +#ifdef QUANT_DEBUG + fprintf(stderr,"ctfa called\n"); +#endif + CHRONO("ctfa"); + + white.r=white.g=white.b=255; + black.r=black.g=black.b=0; + + tbl=img_rehash(NULL,arr->size); + + s2.type=s.type=T_INT; + for (i=0; i<arr->size; i++) + { + array_index(&s,arr,i); + if (s.type!=T_ARRAY || s.u.array->size<3) + { + free(tbl); + error("Illegal type in colorlist, element %d, %s\n",i,from); + } + array_index(&s2,s.u.array,0); + if (s2.type!=T_INT) tbl->tbl[i].rgb.r=0; else tbl->tbl[i].rgb.r=s2.u.integer; + array_index(&s2,s.u.array,1); + if (s2.type!=T_INT) tbl->tbl[i].rgb.g=0; else tbl->tbl[i].rgb.g=s2.u.integer; + array_index(&s2,s.u.array,2); + if (s2.type!=T_INT) tbl->tbl[i].rgb.b=0; else tbl->tbl[i].rgb.b=s2.u.integer; + tbl->tbl[i].count=1; + } + free_svalue(&s); + free_svalue(&s2); + + ct = colortable_allocate(arr->size); + + CHRONO("sort"); + next_free_rgb_node=ct->rgb_node+1; + sort_tbl(tbl, 0, arr->size, 0, 0, arr->size, -1, ct, black, white, + &next_free_rgb_node,ct->rgb_node); + +#ifdef QUANT_DEBUG + fprintf(stderr,"img_quant done, %d colors selected\n", arr->size); +#endif + CHRONO("done"); + + free(tbl); + + for (i=0; i<QUANT_SELECT_CACHE; i++) + ct->cache[i].index=white; + j=colortable_rgb(ct,black); /* ^^ dont find it in the cache... */ + for (i=0; i<QUANT_SELECT_CACHE; i++) + ct->cache[i].index=black, + ct->cache[i].value=j; + + CHRONO("really done"); + THREADS_DISALLOW(); + return ct; +} + + +int colortable_rgb(struct colortable *ct,rgb_group rgb) +{ + int i,best; + + if (ct->cache->index.r==rgb.r && + ct->cache->index.g==rgb.g && + ct->cache->index.b==rgb.b) + return ct->cache->value; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr,"rgb: %d,%d,%d\n",rgb.r,rgb.g,rgb.b); +#endif + +#if QUANT_SELECT_CACHE>1 + for (i=1; i<QUANT_SELECT_CACHE; i++) + if (ct->cache[i].index.r==rgb.r && + ct->cache[i].index.g==rgb.g && + ct->cache[i].index.b==rgb.b) + { + best=ct->cache[i].value; + + MEMMOVE(ct->cache+1,ct->cache, + i*sizeof(struct rgb_cache)); + ct->cache[0].index=rgb; + ct->cache[0].value=best; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr,"cache: %lu: %d,%d,%d\n",best,ct->clut[best].r,ct->clut[best].g,ct->clut[best].b); +#endif + return best; + } +#endif + + /* find node */ + +#if 1 + + do + { + rgb_group min={0,0,0},max={255,255,255}; + unsigned long *rn; + unsigned char split; + + rn=ct->rgb_node; + + for (;;) + { +#ifdef QUANT_DEBUG_RGB + fprintf(stderr,"-> %d: %c%d %d\n", + rn-ct->rgb_node, + (((*rn)>>30)==0)?'c': + (((*rn)>>30)==1)?'r': + (((*rn)>>30)==2)?'g':'b', + ((*rn)>>22) & 255, + ((*rn)&4194303)); +#endif + + switch ((*rn)>>30) + { + case 0: /* end node */ break; + case 1: /* red */ + split=(unsigned char)( ((*rn)>>22) & 255 ); + rn=ct->rgb_node+((*rn)&4194303); + if (rgb.r<=split) max.r=split; + else rn++,min.r=split+1; + continue; + case 2: /* green */ + split=(unsigned char)( ((*rn)>>22) & 255 ); + rn=ct->rgb_node+((*rn)&4194303); + if (rgb.g<=split) max.g=split; + else rn++,min.g=split+1; + continue; + case 3: /* blue */ + split=(unsigned char)( ((*rn)>>22) & 255 ); + rn=ct->rgb_node+((*rn)&4194303); + if (rgb.b<=split) max.b=split; + else rn++,min.b=split+1; + continue; + } + break; + } + best=*rn; + } + while (0); + +#endif + + /* place in cache */ +#if QUANT_SELECT_CACHE>1 + MEMMOVE(ct->cache+1,ct->cache, + (QUANT_SELECT_CACHE-1)*sizeof(struct rgb_cache)); +#endif + ct->cache[0].index=rgb; + ct->cache[0].value=best; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr," -> %lu: %d,%d,%d\n",best, + ct->clut[best].r,ct->clut[best].g,ct->clut[best].b); +#endif + return best; +} + +int colortable_rgb_nearest(struct colortable *ct,rgb_group rgb) +{ + int i,best=0,di,di2; + rgb_group *prgb; + + if (ct->cache->index.r==rgb.r && + ct->cache->index.g==rgb.g && + ct->cache->index.b==rgb.b) + return ct->cache->value; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr,"rgb_n: #%02x%02x%02x; ",rgb.r,rgb.g,rgb.b); +#endif + +#if QUANT_SELECT_CACHE>1 + for (i=1; i<QUANT_SELECT_CACHE; i++) + if (ct->cache[i].index.r==rgb.r && + ct->cache[i].index.g==rgb.g && + ct->cache[i].index.b==rgb.b) + { + best=ct->cache[i].value; + + MEMMOVE(ct->cache+1,ct->cache, + i*sizeof(struct rgb_cache)); + ct->cache[0].index=rgb; + ct->cache[0].value=best; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr,"cache: %lu: %d,%d,%d\n",best,ct->clut[best].r,ct->clut[best].g,ct->clut[best].b); +#endif + return best; + } +#endif + + /* find node */ + + di=1000000L; + for (i=0; i<ct->numcol; i++) + { + prgb=ct->clut+i; + if ((di2=DISTANCE(*prgb,rgb))<di) + { + best=i; + di=di2; +#ifdef QUANT_DEBUG_RGB + fprintf(stderr,"#%02x%02x%02x (%d) (%d), ", + ct->clut[i].r, + ct->clut[i].g, + ct->clut[i].b, + i,di); +#endif + } + } +#ifdef QUANT_DEBUG_RGB + fprintf(stderr,"\n"); +#endif + + /* place in cache */ +#if QUANT_SELECT_CACHE>1 + MEMMOVE(ct->cache+1,ct->cache, + (QUANT_SELECT_CACHE-1)*sizeof(struct rgb_cache)); +#endif + ct->cache[0].index=rgb; + ct->cache[0].value=best; + +#ifdef QUANT_DEBUG_RGB +fprintf(stderr," -> %lu: %d,%d,%d\n",best, + ct->clut[best].r,ct->clut[best].g,ct->clut[best].b); +#endif + return best; +} + +void colortable_free(struct colortable *ct) +{ + int r,g,b; + free((char *)ct->rgb_node); + free((char *)ct); +} diff --git a/src/modules/Image/testfont b/src/modules/Image/testfont new file mode 100644 index 0000000000000000000000000000000000000000..3705501ac48434af2a765ba61eee5a6718d3540e Binary files /dev/null and b/src/modules/Image/testfont differ diff --git a/src/modules/Image/testsuite.in b/src/modules/Image/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..b8a35edf849d18b3b0ca44ca915ed7063d3b1e7a --- /dev/null +++ b/src/modules/Image/testsuite.in @@ -0,0 +1,52 @@ +cond([[ master()->programs["/precompiled/image"] ]], +[[ + test_true(programp(Image)) + test_true(objectp(clone(Image))) + test_true(objectp(clone(Image,10,10))) + test_true(objectp(clone(Image,10,10,1,1,1))) + test_eq(clone(Image,10,12)->xsize(),10)) + test_eq(clone(Image,10,12)->ysize(),12)) + test_true(objectp(clone(Image,10,10,1,1,1)->copy())) + test_eq(clone(Image,10,12)->copy()->xsize(),10)) + test_eq(clone(Image,10,12)->copy()->ysize(),12)) +dnl test_true(objectp(clone(Image,10,10)->crop(2,2,3,3))) + test_true(objectp(clone(Image,10,10)->autocrop())) + test_true(objectp(clone(Image,10,10)->gray())) + test_true(objectp(clone(Image,10,10)->color(2,2,4))) + test_true(objectp(clone(Image,10,10)->invert())) + test_true(objectp(clone(Image,10,10)->threshold(10,20,30))) +dnl matrix + test_true(objectp(clone(Image,10,10)->scale(2.1))) + test_eq(clone(Image,10,12)->scale(2.0)->xsize(),20) + test_eq(clone(Image,10,12)->scale(2.0)->ysize(),24) + test_true(objectp(clone(Image,10,10)->scale(0.2))) + test_true(objectp(clone(Image,10,10)->scale(2.0,0.2))) + test_true(objectp(clone(Image,10,10)->scale(0.2,2.2))) + test_true(objectp(clone(Image,10,10)->scale(0.2,2.2))) + test_true(objectp(clone(Image,10,10)->scale(0.2,2.2))) + test_eq(clone(Image,10,10)->scale(33,57)->xsize(),33) + test_eq(clone(Image,10,10)->scale(33,57)->ysize(),57) + test_true(stringp(clone(Image,10,10)->toppm())) +dnl fromppm + test_true(stringp(clone(Image,10,10)->toppm())) + test_true(stringp(clone(Image,10,10)->togif())) + test_true(objectp(clone(Image,10,10)->paste(clone(Image,3,3),2,3))) +dnl past_alpha +dnl past_mask + test_do(clone(Image,10,10)->setcolor(2,2,2)) + test_do(clone(Image,10,10)->setpixel(2,2,2,2,2)) + test_do(clone(Image,10,10)->line(2,2,4,4,2,2,2)) + test_do(clone(Image,10,10)->box(2,2,4,4,2,2,2)) + test_do(clone(Image,10,10)->circle(2,2,4,4,2,2,2)) +dnl tuned_box + test_eq(clone(Image,10,10)->xsize(),10) + test_eq(clone(Image,10,10)->ysize(),10) + test_do(clone(Font)) + test_do(clone(Font)->load("SRCDIR/testfont")) + test_any(object o=clone(Font); o->load("SRCDIR/testfont"); return o->height(),19) + test_any(object o=clone(Font); o->load("SRCDIR/testfont"); return o->write("foo")->xsize(),23) + test_any(object o=clone(Font); o->load("SRCDIR/testfont"); return +o->write("foo")->ysize(),20) +dnl test_any(object o=clone(Font); o->load("SRCDIR/testfont"); o->write("foo"); clone(Image,100,100)->paste_alpha(o); return 1,1) +]]) + diff --git a/src/modules/Image/togif.c b/src/modules/Image/togif.c new file mode 100644 index 0000000000000000000000000000000000000000..7cde3f906c843b22c0946e4b6aba98d50bc95586 --- /dev/null +++ b/src/modules/Image/togif.c @@ -0,0 +1,778 @@ +/* + +togif + +Pontus Hagland, law@infovav.se + +$Id: togif.c,v 1.1 1997/02/11 08:35:47 hubbe Exp $ + +*/ + +#include "global.h" + +#include <math.h> +#include <ctype.h> + +#include "stralloc.h" +#include "global.h" +#include "threads.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "array.h" +#include "error.h" +#include "dynamic_buffer.h" + +#include "image.h" +#include "lzw.h" + +#define INITIAL_BUF_LEN 8192 + +#define THIS ((struct image *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +#if 0 +#include <sys/resource.h> +#define CHRONO(X) chrono(X) + +static void chrono(char *x) +{ + struct rusage r; + static struct rusage rold; + getrusage(RUSAGE_SELF,&r); + fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x, + r.ru_utime.tv_sec,r.ru_utime.tv_usec, + + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0) + +r.ru_utime.tv_sec-rold.ru_utime.tv_sec, + ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0) + + r.ru_utime.tv_usec-rold.ru_utime.tv_usec + ); + + rold=r; +} +#else +#define CHRONO(X) +#endif + + +#define min(a,b) ((a)<(b)?(a):(b)) +#define max(a,b) ((a)<(b)?(b):(a)) +#define testrange(x) max(min((x),255),0) + +static void buf_word( unsigned short w, dynamic_buffer *buf ) +{ + low_my_putchar( w&0xff, buf ); + low_my_putchar( (w>>8)&0xff, buf ); +} + +#define WEIGHT_NEXT(X) (((X)*8)/20) +#define WEIGHT_DOWNNEXT(X) (((X)*3)/20) +#define WEIGHT_DOWN(X) (((X)*3)/20) +#define WEIGHT_DOWNBACK(X) (((X)*0)/20) + +static int floyd_steinberg_add(rgbl_group *errl, + rgbl_group *errlfwd, + rgbl_group *errlback, + rgbl_group *err, + rgb_group rgb, + struct colortable *ct) +{ + rgb_group rgb2,rgb3; + rgbl_group cerr; + int c; + rgb2.r=testrange((long)rgb.r+err->r/FS_SCALE); + rgb2.g=testrange((long)rgb.g+err->g/FS_SCALE); + rgb2.b=testrange((long)rgb.b+err->b/FS_SCALE); +#ifdef FS_DEBUG + fprintf(stderr,"%g,%g,%g+%g,%g,%g=%g,%g,%g ", + 1.0*rgb.r, 1.0*rgb.g, 1.0*rgb.b, + err->r*1.0/FS_SCALE, err->g*1.0/FS_SCALE, err->b*1.0/FS_SCALE, + rgb2.r*1.0, rgb2.g*1.0, rgb2.b*1.0); +#endif + c=colortable_rgb(ct,rgb2); + rgb3=ct->clut[c]; + cerr.r=(long)rgb.r*FS_SCALE-(long)rgb3.r*FS_SCALE+err->r; + cerr.g=(long)rgb.g*FS_SCALE-(long)rgb3.g*FS_SCALE+err->g; + cerr.b=(long)rgb.b*FS_SCALE-(long)rgb3.b*FS_SCALE+err->b; + +#ifdef FS_DEBUG + fprintf(stderr,"got %g,%g,%g err %g,%g,%g ", + 1.0*rgb3.r, + 1.0*rgb3.g, + 1.0*rgb3.b, + 1.0*cerr.r, + 1.0*cerr.g, + 1.0*cerr.b); +#endif + + errl->r+=WEIGHT_DOWN(cerr.r); + errl->g+=WEIGHT_DOWN(cerr.g); + errl->b+=WEIGHT_DOWN(cerr.b); + if (errlback) + { + errlback->r+=WEIGHT_DOWNBACK(cerr.r); + errlback->g+=WEIGHT_DOWNBACK(cerr.g); + errlback->b+=WEIGHT_DOWNBACK(cerr.b); +#ifdef FS_DEBUG + fprintf(stderr,"errlback=>%g ",errlback->g*1.0/FS_SCALE); +#endif + } + if (errlfwd) + { + err->r=WEIGHT_NEXT(cerr.r); + err->g=WEIGHT_NEXT(cerr.g); + err->b=WEIGHT_NEXT(cerr.b); + err->r+=errlfwd->r; + err->g+=errlfwd->g; + err->b+=errlfwd->b; + errlfwd->r=WEIGHT_DOWNNEXT(cerr.r); + errlfwd->g=WEIGHT_DOWNNEXT(cerr.g); + errlfwd->b=WEIGHT_DOWNNEXT(cerr.b); +#ifdef FS_DEBUG + fprintf(stderr,"errlfwd=>%g ",errlfwd->g*1.0/FS_SCALE); +#endif + } +#ifdef FS_DEBUG + fprintf(stderr,"errl=>%g ",errl->g*1.0/FS_SCALE); + fprintf(stderr,"err=>%g\n",err->g*1.0/FS_SCALE); +#endif + return c; +} + +void image_floyd_steinberg(rgb_group *rgb,int xsize, + rgbl_group *errl, + int way,int *res, + struct colortable *ct) +{ + rgbl_group err; + int x; + + if (way) + { + err.r=errl[xsize-1].r; + err.g=errl[xsize-1].g; + err.b=errl[xsize-1].b; + for (x=xsize-1; x>=0; x--) + res[x]=floyd_steinberg_add(errl+x, + (x==0)?NULL:errl+x-1, + (x==xsize-1)?NULL:errl+x+1, + &err,rgb[x],ct); + } + else + { + err.r=errl->r; + err.g=errl->g; + err.b=errl->b; + for (x=0; x<xsize; x++) + res[x]=floyd_steinberg_add(errl+x, + (x==xsize-1)?NULL:errl+x+1, + (x==0)?NULL:errl+x-1, + &err,rgb[x],ct); + } +} + + +struct pike_string * + image_encode_gif(struct image *img,struct colortable *ct, + rgb_group *transparent,int fs) +{ + dynamic_buffer buf; + long i; + rgb_group *rgb; + struct lzw lzw; + int colors,bpp; + +CHRONO("image_encode_gif begin"); + + buf.s.str=NULL; + initialize_buf(&buf); + + colors=4; bpp=2; + while (colors<ct->numcol) { colors<<=1; bpp++; } + + low_my_binary_strcat(transparent?"GIF89a":"GIF87a",6,&buf); + buf_word((unsigned short)img->xsize,&buf); + buf_word((unsigned short)img->ysize,&buf); + low_my_putchar( (char)(0xf0|(bpp-1)), &buf); + /* | global colormap | 3 bits color res | sort | 3 bits bpp */ + /* color res is'nt cared of */ + + low_my_putchar( 0, &buf ); /* background color */ + low_my_putchar( 0, &buf ); /* just zero */ + + for (i=0; i<ct->numcol; i++) + { + low_my_putchar(ct->clut[i].r,&buf); + low_my_putchar(ct->clut[i].g,&buf); + low_my_putchar(ct->clut[i].b,&buf); + } + + for (; i<colors; i++) + { + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + } + + if (transparent) + { + i=colortable_rgb(ct,*transparent); + + low_my_putchar( '!', &buf ); /* extras */ + low_my_putchar( 0xf9, &buf ); /* transparency */ + low_my_putchar( 4, &buf ); + low_my_putchar( 1, &buf ); + low_my_putchar( 0, &buf ); + low_my_putchar( 0, &buf ); + low_my_putchar( i, &buf ); + low_my_putchar( 0, &buf ); + } + + + low_my_putchar( ',', &buf ); /* image separator */ + + buf_word(0,&buf); /* leftofs */ + buf_word(0,&buf); /* topofs */ + buf_word(img->xsize,&buf); /* width */ + buf_word(img->ysize,&buf); /* height */ + + low_my_putchar(0x00, &buf); + /* not interlaced (interlaced == 0x40) */ + /* no local colormap ( == 0x80) */ + + low_my_putchar( bpp, &buf ); /* bits per pixel , or min 2 */ + + i=img->xsize*img->ysize; + rgb=img->img; + +CHRONO("image_encode_gif header done"); + +THREADS_ALLOW(); + lzw_init(&lzw,bpp); + if (!fs) + while (i--) lzw_add(&lzw,colortable_rgb(ct,*(rgb++))); + else + { + rgbl_group *errb; + rgb_group corgb; + int w,*cres,j; + errb=(rgbl_group*)xalloc(sizeof(rgbl_group)*img->xsize); + cres=(int*)xalloc(sizeof(int)*img->xsize); + for (i=0; i<img->xsize; i++) + errb[i].r=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].g=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].b=(rand()%(FS_SCALE*2+1))-FS_SCALE; + + w=0; + i=img->ysize; + while (i--) + { + image_floyd_steinberg(rgb,img->xsize,errb,w=!w,cres,ct); + for (j=0; j<img->xsize; j++) + lzw_add(&lzw,cres[j]); + rgb+=img->xsize; + } + + free(errb); + free(cres); + } + + lzw_write_last(&lzw); + +CHRONO("lzw done"); + + for (i=0; i<(int)lzw.outpos; i+=254) + { + int wr; + if (i+254>(int)lzw.outpos) wr=lzw.outpos-i; + else wr=254; + low_my_putchar( (unsigned char)wr, &buf ); /* bytes in chunk */ + low_my_binary_strcat( (char *) lzw.out+i, wr, &buf ); + } + low_my_putchar( 0, &buf ); /* terminate stream */ + +CHRONO("image_encode_gif wrote ok"); + + lzw_quit(&lzw); + + low_my_putchar( ';', &buf ); /* end gif file */ + +CHRONO("image_encode_gif done"); +THREADS_DISALLOW(); + + return low_free_buf(&buf); +} + +#define STD_ARENA_SIZE 16384 + +int image_decode_gif(struct image *dest,struct image *dest_alpha, + unsigned char *src,unsigned long len) +{ + unsigned char *arena,*tmpstore,*q; + rgb_group *global_palette,*palette; + rgb_group *rgb; + int bpp; + unsigned long i,j; + INT32 mod; + INT32 leftofs,topofs,width,height; + int interlaced,transparent; + unsigned long arenalen,pos; + + if (src[0]!='G' + ||src[1]!='I' + ||src[2]!='F' + ||len<12) + return 0; /* not a gif, you fool */ + + dest->xsize=src[6]+(src[7]<<8); + dest->ysize=src[8]+(src[9]<<8); + + if (! (arena=malloc(arenalen=STD_ARENA_SIZE)) ) return 0; + + dest->img=malloc(dest->xsize*dest->ysize*sizeof(rgb_group)); + if (!dest->img) { free(arena); return 0; } + /* out of memory (probably illegal size) */ + + bpp=(src[10]&7)+1; + + THREADS_ALLOW(); + if (src[10]&128) + { + global_palette=(rgb_group*)(src+13); + src+=3*(1<<bpp); + len-=3*(1<<bpp); + rgb=dest->img; + i=dest->xsize*dest->ysize; +/* + while (i--) + *rgb=global_palette[src[11]]; * paint with background color */ + + } + else + global_palette=NULL; + MEMSET(dest->img,0,sizeof(rgb_group)*dest->xsize*dest->ysize); + src+=13; len-=13; + + do + { + switch (*src) + { + case '!': /* function block */ + if (len<7) break; /* no len left */ + if (src[1]==0xf9) + transparent=src[6]; + len-=src[3]+1; + src+=src[3]+1; + continue; + case ',': /* image block(s) */ + if (len<10) len-=10; /* no len left */ + leftofs=src[1]+(src[2]<<8); + topofs=src[3]+(src[4]<<8); + width=src[5]+(src[6]<<8); + height=src[7]+(src[8]<<8); + interlaced=src[9]&64; + + if (src[9]&128) + { + palette=(rgb_group*)(src+10); + src+=((src[9]&7)+1)*3; + } + else + palette=global_palette; + + src+=11; + len-=11; + pos=0; + if (len<3) break; /* no len left */ + bpp=src[-1]; + + while (len>1) + { + if (!(i=*src)) break; /* length of block */ + if (pos+i>arenalen) + { + arena=realloc(arena,arenalen*=2); + if (!arena) return 1; + } + MEMCPY(arena+pos,src+1,min(i,len-1)); + pos+=min(i,len-1); + len-=i+1; + src+=i+1; + } + + if (leftofs+width<=dest->xsize && topofs+height<=dest->ysize) + { + tmpstore=malloc(width*height); + if (!tmpstore) break; + i=lzw_unpack(tmpstore,width*height,arena,pos,bpp); + if (i!=(unsigned long)(width*height)) + MEMSET(tmpstore+i,0,width*height-i); + rgb=dest->img+leftofs+topofs*dest->ysize; + mod=width-dest->xsize; + j=height; + q=tmpstore; + if (palette) + while (j--) + { + i=width; + while (i--) *(rgb++)=palette[*(q++)]; + rgb+=mod; + } + free(tmpstore); + } + + continue; + case ';': + + break; /* file done */ + default: + break; /* unknown, file is ok? */ + } + } + while (0); + THREADS_DISALLOW(); + + if (arena) free(arena); + return 1; /* ok */ +} + + + +void image_fromgif(INT32 args) +{ + if (sp[-args].type!=T_STRING) + error("Illegal argument to image->fromgif()\n"); + + if (THIS->img) free(THIS->img); + THIS->img=NULL; + + image_decode_gif(THIS,NULL,sp[-args].u.string->str,sp[-args].u.string->len); + + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); +} + +static INLINE void getrgb(struct image *img, + INT32 args_start,INT32 args,char *name) +{ + INT32 i; + if (args-args_start<3) return; + for (i=0; i<3; i++) + if (sp[-args+i+args_start].type!=T_INT) + error("Illegal r,g,b argument to %s\n",name); + img->rgb.r=(unsigned char)sp[-args+args_start].u.integer; + img->rgb.g=(unsigned char)sp[1-args+args_start].u.integer; + img->rgb.b=(unsigned char)sp[2-args+args_start].u.integer; + if (args-args_start>=4) + if (sp[3-args+args_start].type!=T_INT) + error("Illegal alpha argument to %s\n",name); + else + img->alpha=sp[3-args+args_start].u.integer; + else + img->alpha=0; +} + +void image_togif(INT32 args) +{ + rgb_group *transparent=NULL; + struct colortable *ct=NULL; + + if (args>0 && sp[-args].type==T_ARRAY) + ct=colortable_from_array(sp[-args].u.array,"image->togif()\n"); + else if (args>0 && args!=3 && sp[-args].type==T_INT) + ct=colortable_quant(THIS,min(256,max(2,sp[-args].u.integer))); + + if (args>=3+!!ct) + { + getrgb(THIS,!!ct,args,"image->togif() (transparency)"); + transparent=&(THIS->rgb); + } + + pop_n_elems(args); + if (!THIS->img) { error("no image\n"); return; } + + if (!ct) ct=colortable_quant(THIS,256); + push_string( image_encode_gif( THIS,ct, transparent, 0) ); + colortable_free(ct); +} + + +void image_togif_fs(INT32 args) +{ + rgb_group *transparent=NULL; + struct colortable *ct=NULL; + + if (args>0 && sp[-args].type==T_ARRAY) + ct=colortable_from_array(sp[-args].u.array,"image->togif_fs()\n"); + else if (args>0 && args!=3 && sp[-args].type==T_INT) + ct=colortable_quant(THIS,min(256,max(2,sp[-args].u.integer))); + + if (args>=3+!!ct) + { + getrgb(THIS,!!ct,args,"image->togif() (transparency)"); + transparent=&(THIS->rgb); + } + + pop_n_elems(args); + if (!THIS->img) { error("no image\n"); return; } + + if (!ct) + ct=colortable_quant(THIS,256); + push_string( image_encode_gif( THIS,ct, transparent, 1) ); + colortable_free(ct); +} + +void image_gif_begin(INT32 args) +{ + dynamic_buffer buf; + long i; + int colors,bpp; + struct colortable *ct=NULL; + + if (args) + if (sp[-args].type==T_INT && sp[-args].u.integer!=0) + ct=colortable_quant(THIS,max(256,min(2,sp[-args].u.integer))); + else if (sp[-args].type==T_ARRAY) + ct=colortable_from_array(sp[-args].u.array,"image->gif_begin()\n"); + + pop_n_elems(args); + + buf.s.str=NULL; + initialize_buf(&buf); + + if (ct) + { + colors=4; bpp=2; + while (colors<ct->numcol) { colors<<=1; bpp++; } + } + else + { + colors=256; bpp=8; + } + + low_my_binary_strcat("GIF89a",6,&buf); + buf_word((unsigned short)THIS->xsize,&buf); + buf_word((unsigned short)THIS->ysize,&buf); + low_my_putchar( (char)((0x80*!!ct) | 0x70 | (bpp-1) ), &buf); + /* | global colormap | 3 bits color res | sort | 3 bits bpp */ + /* color res is'nt cared of */ + + low_my_putchar( 0, &buf ); /* background color */ + low_my_putchar( 0, &buf ); /* just zero */ + + if (!!ct) + { + for (i=0; i<ct->numcol; i++) + { + low_my_putchar(ct->clut[i].r,&buf); + low_my_putchar(ct->clut[i].g,&buf); + low_my_putchar(ct->clut[i].b,&buf); + } + + for (; i<colors; i++) + { + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + } + } + push_string(low_free_buf(&buf)); +} + +void image_gif_end(INT32 args) +{ + pop_n_elems(args); + push_string(make_shared_binary_string(";",1)); +} + +void image_gif_netscape_loop(INT32 args) +{ + unsigned short loops=0; + char buf[30]; + if (args) + if (sp[-args].type!=T_INT) + error("Illegal argument to image->gif_netscape_loop()\n"); + else + loops=sp[-args].u.integer; + else + loops=65535; + pop_n_elems(args); + + sprintf(buf,"%c%c%cNETSCAPE2.0%c%c%c%c%c", + 33,255,11,3,1,loops&255,(loops>>8)&255,0); + + push_string(make_shared_binary_string(buf,19)); +} + +static void img_gif_add(INT32 args,int fs,int lm) +{ + INT32 x=0,y=0,i; + struct lzw lzw; + rgb_group *rgb; + struct colortable *ct=NULL; + dynamic_buffer buf; + int colors,bpp; + +CHRONO("gif add init"); + + buf.s.str=NULL; + initialize_buf(&buf); + + if (args==0) x=y=0; + else if (sp[-args].type!=T_INT + || sp[1-args].type!=T_INT) + error("Illegal argument(s) to image->gif_add()\n"); + else + { + x=sp[-args].u.integer; + y=sp[1-args].u.integer; + } + + + if (args>2 && sp[2-args].type==T_ARRAY) + ct=colortable_from_array(sp[2-args].u.array,"image->gif_add()\n"); + else if (args>3 && sp[2-args].type==T_INT) + ct=colortable_quant(THIS,max(256,min(2,sp[2-args].u.integer))); + + if (args>2+!!ct) + { + unsigned short delay=0; + if (sp[2+!!ct-args].type==T_INT) + delay=sp[2+!!ct-args].u.integer; + else if (sp[2+!!ct-args].type==T_FLOAT) + delay=(unsigned short)(sp[2+!!ct-args].u.float_number*100); + else + error("Illegal argument %d to image->gif_add()\n",3+!!ct); + + low_my_putchar( '!', &buf ); /* extension block */ + low_my_putchar( 0xf9, &buf ); /* graphics control */ + low_my_putchar( 4, &buf ); /* block size */ + low_my_putchar( 0, &buf ); /* disposal, transparency, blabla */ + buf_word( delay, &buf ); /* delay in centiseconds */ + low_my_putchar( 0, &buf ); /* (transparency index) */ + low_my_putchar( 0, &buf ); /* terminate block */ + } + + ct=colortable_quant(THIS,256); + + colors=4; bpp=2; + while (colors<ct->numcol) { colors<<=1; bpp++; } + + + + + low_my_putchar( ',', &buf ); /* image separator */ + + buf_word(x,&buf); /* leftofs */ + buf_word(y,&buf); /* topofs */ + buf_word(THIS->xsize,&buf); /* width */ + buf_word(THIS->ysize,&buf); /* height */ + + low_my_putchar((0x80*lm)|(bpp-1), &buf); + /* not interlaced (interlaced == 0x40) */ + /* local colormap ( == 0x80) */ + /* 8 bpp in map ( == 0x07) */ + + if (lm) + { + for (i=0; i<ct->numcol; i++) + { + low_my_putchar(ct->clut[i].r,&buf); + low_my_putchar(ct->clut[i].g,&buf); + low_my_putchar(ct->clut[i].b,&buf); + } + for (; i<colors; i++) + { + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + low_my_putchar(0,&buf); + } + } + + low_my_putchar( bpp, &buf ); + + i=THIS->xsize*THIS->ysize; + rgb=THIS->img; + +CHRONO("begin pack"); + + THREADS_ALLOW(); + lzw_init(&lzw,bpp); + if (!fs) + while (i--) lzw_add(&lzw,colortable_rgb(ct,*(rgb++))); + else + { + rgbl_group *errb; + rgb_group corgb; + int w,*cres,j; + errb=(rgbl_group*)xalloc(sizeof(rgbl_group)*THIS->xsize); + cres=(int*)xalloc(sizeof(int)*THIS->xsize); + for (i=0; i<THIS->xsize; i++) + errb[i].r=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].g=(rand()%(FS_SCALE*2+1))-FS_SCALE, + errb[i].b=(rand()%(FS_SCALE*2+1))-FS_SCALE; + + w=0; + i=THIS->ysize; + while (i--) + { + image_floyd_steinberg(rgb,THIS->xsize,errb,w=!w,cres,ct); + for (j=0; j<THIS->xsize; j++) + lzw_add(&lzw,cres[j]); + rgb+=THIS->xsize; + } + + free(errb); + free(cres); + } + + lzw_write_last(&lzw); + +CHRONO("end pack"); + + for (i=0; i<(int)lzw.outpos; i+=254) + { + int wr; + if (i+254>(int)lzw.outpos) wr=lzw.outpos-i; + else wr=254; + low_my_putchar( (unsigned char)wr, &buf ); /* bytes in chunk */ + low_my_binary_strcat( (char *) lzw.out+i, wr, &buf ); + } + low_my_putchar( 0, &buf ); /* terminate stream */ + + lzw_quit(&lzw); + + colortable_free(ct); + THREADS_DISALLOW(); + +CHRONO("done"); + + pop_n_elems(args); + push_string(low_free_buf(&buf)); +} + +void image_gif_add(INT32 args) +{ + img_gif_add(args,0,1); +} + +void image_gif_add_fs(INT32 args) +{ + img_gif_add(args,1,1); +} + +void image_gif_add_nomap(INT32 args) +{ + img_gif_add(args,0,0); +} + +void image_gif_add_fs_nomap(INT32 args) +{ + img_gif_add(args,1,0); +} + diff --git a/src/modules/Mysql/.cvsignore b/src/modules/Mysql/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..323c193f0fa8587cc986797a6352e9c54067ff58 --- /dev/null +++ b/src/modules/Mysql/.cvsignore @@ -0,0 +1,9 @@ +.pure +Makefile +config.h.in +config.log +config.status +configure +dependencies +linker_options +stamp-h diff --git a/src/modules/Mysql/.gitignore b/src/modules/Mysql/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..5a77561f9be70aacc6af6ea6fa9c40ea15a85a9b --- /dev/null +++ b/src/modules/Mysql/.gitignore @@ -0,0 +1,9 @@ +/.pure +/Makefile +/config.h.in +/config.log +/config.status +/configure +/dependencies +/linker_options +/stamp-h diff --git a/src/modules/Mysql/Makefile.in b/src/modules/Mysql/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..f8992d2d43f64ce07fa3a9d7edcec98d13aec3c3 --- /dev/null +++ b/src/modules/Mysql/Makefile.in @@ -0,0 +1,12 @@ +# +# $Id: Makefile.in,v 1.1 1997/02/11 08:36:33 hubbe Exp $ +# + +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@ +OBJS=mysql.o result.o +MODULE_LDFLAGS=@LDFLAGS@ @MYSQL_LIBS@ + +@dynamic_module_makefile@ +@dependencies@ \ No newline at end of file diff --git a/src/modules/Mysql/acconfig.h b/src/modules/Mysql/acconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..7ca0acfc0291de0d56464fc0ff1111969f3c7f55 --- /dev/null +++ b/src/modules/Mysql/acconfig.h @@ -0,0 +1,18 @@ +/* + * $Id: acconfig.h,v 1.1 1997/02/11 08:36:34 hubbe Exp $ + * + * Config-file for the Pike mySQL-module. + * + * Henrik Grubbstr�m 1997-01-30 + */ + +#ifndef PIKE_MYSQL_CONFIG_H +#define PIKE_MYSQL_CONFIG_H + +@TOP@ +@BOTTOM@ + +/* Define if you have mySQL */ +#undef HAVE_MYSQL + +#endif /* PIKE_MYSQL_CONFIG_H */ diff --git a/src/modules/Mysql/configure.in b/src/modules/Mysql/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..0231e41d1dbee73a10b31acccb3ce3f297ea1f3c --- /dev/null +++ b/src/modules/Mysql/configure.in @@ -0,0 +1,192 @@ +# +# $Id: configure.in,v 1.1 1997/02/11 08:36:34 hubbe Exp $ +# +# Configure script for the mysql-module +# +# Henrik Grubbstr�m +# + +# +# NOTE: +# Prior to 3.20.0 After 3.20.0 +# -------------------------------------------------------------- +# /usr/local/mysql/mach-lib-threads /usr/local/lib/mysql +# /usr/local/mysql/include /usr/local/include/mysql +# libmysql.a libmysqllib.a +# libstrings.a libmystrings.a +# + + +AC_INIT(mysql.c) +AC_CONFIG_HEADER(config.h) + +sinclude(../module_configure.in) + +OLD_LIBS=$LIBS +OLD_LDFLAGS=$LDFLAGS +OLD_CPPFLAGS=$CPPFLAGS +MYSQL_LIBS="" + +AC_ARG_WITH(mysql, [ --without-mysql no support for the Mysql database],[],[with_mysql=yes]) + +if test x$with_mysql = xyes; then + + AC_MSG_CHECKING(Checking for Mysql lib-directory) + + AC_CACHE_VAL(pike_cv_mysql_lib_dir, [ + for pike_cv_mysql_lib_dir in /usr/local/lib/mysql /usr/local/mysql/lib/mysql /usr/gnu/lib/mysql /usr/lib/mysql /lib/mysql /usr/local/mysql/lib /usr/local/mysql/mach-lib-thread no; do + if test -d $pike_cv_mysql_lib_dir/.; then + break + else + : + fi + done + ]) + + AC_MSG_RESULT($pike_cv_mysql_lib_dir) + + if test x$pike_cv_mysql_lib_dir = xno; then :; else + echo Adding $pike_cv_mysql_lib_dir to the library search path. + LDFLAGS="-L$pike_cv_mysql_lib_dir ${LDFLAGS}" + fi + + AC_MSG_CHECKING(Checking for Mysql include-directory) + + AC_CACHE_VAL(pike_cv_mysql_include_dir, [ + for pike_cv_mysql_include_dir in /usr/local/include/mysql /usr/local/mysql/include/mysql /usr/gnu/include/mysql /usr/include/mysql /include/mysql /usr/local/mysql/include no; do + if test -d $pike_cv_mysql_include_dir/.; then + break + else + : + fi + done + ]) + + AC_MSG_RESULT($pike_cv_mysql_include_dir) + + if test x$pike_cv_mysql_include_dir = xno; then :; else + echo Adding $pike_cv_mysql_include_dir to the include search path. + CPPFLAGS="-I$pike_cv_mysql_include_dir ${CPPFLAGS}" + fi + + # Header file + + AC_CHECK_HEADERS(mysql.h mysql/mysql.h) + + # Mysql libs + + pike_cv_mysql="no" + + AC_CHECK_LIB(mystrings, bchange, [ + LIBS="-lmystrings $LIBS" + MYSQL_LIBS="-lmystrings ${MYSQL_LIBS}" + pike_cv_mysql="post3.20" + ], [ + AC_CHECK_LIB(strings, bchange, [ + LIBS="-lstrings $LIBS" + MYSQL_LIBS="-lstrings ${MYSQL_LIBS}" + pike_cv_mysql="pre3.20" + ], []) + ]) + + AC_MSG_CHECKING(Mysql version) + + AC_MSG_RESULT($pike_cv_mysql) + + if test x$pike_cv_mysql = xno; then + # Restore variables, so we don't link with unnessesary libs + + LIBS=$OLD_LIBS + CPPFLAGS=$OLD_CPPFLAGS + LDFLAGS=$OLD_LDFLAGS + MYSQL_LIBS="" + else + + # System libs which might be needed + + if echo $LIBS|grep -- -lsocket >&5 2>&5; then + : + else + AC_CHECK_LIB(socket, socket, [ + LIBS="-lsocket $LIBS" + MYSQL_LIBS="-lsocket ${MYSQL_LIBS}" + ], []) + fi + if echo $LIBS|grep -- -lnsl >&5 2>&5; then + : + else + AC_CHECK_LIB(nsl, gethostbyname, [ + LIBS="-lnsl $LIBS" + MYSQL_LIBS="-lnsl ${MYSQL_LIBS}" + ], []) + fi + if echo $LIBS|grep -- -lm >&5 2>&5; then + : + else + AC_CHECK_LIB(m, floor, [ + LIBS="-lm $LIBS" + MYSQL_LIBS="-lm ${MYSQL_LIBS}" + ], []) + fi + + # Pthreads is still needed in 3.20.0. + AC_CHECK_FUNC(pthread_self, [], [ + AC_CHECK_LIB(pthread, pthread_self, [ + LIBS="-lpthread $LIBS" + echo Warning added -lpthread to \$LIBS\! + ], [ + AC_CHECK_LIB(pthreads, pthread_self, [ + LIBS="-lpthreads $LIBS" + echo Warning added -lpthreads to \$LIBS\! + ], []) + ]) + ]) + + AC_CHECK_LIB(dbug, _db_doprnt_, [ + LIBS="-ldbug $LIBS" + MYSQL_LIBS="-ldbug ${MYSQL_LIBS}" + ], []) + + AC_CHECK_LIB(mysys, my_init, [ + LIBS="-lmysys $LIBS" + MYSQL_LIBS="-lmysys ${MYSQL_LIBS}" + ], []) + + # Try a couple of mysqlclient libs + # in order of age, newest first. + + AC_CHECK_LIB(mysqlclient, mysql_connect, [ + LIBS="-lmysqlclient $LIBS" + MYSQL_LIBS="-lmysqlclient ${MYSQL_LIBS}" + ], [ + AC_CHECK_LIB(mysqllib, mysql_connect, [ + LIBS="-lmysqllib $LIBS" + MYSQL_LIBS="-lmysqllib ${MYSQL_LIBS}" + ], [ + AC_CHECK_LIB(mysql, mysql_connect, [ + LIBS="-lmysql $LIBS" + MYSQL_LIBS="-lmysql ${MYSQL_LIBS}" + ], [ pike_cv_mysql="no" ]) + ]) + ]) + + if test x$pike_cv_mysql = xno; then + # Restore variables, so we don't link with unnessesary libs + + LIBS=$OLD_LIBS + CPPFLAGS=$OLD_CPPFLAGS + LDFLAGS=$OLD_LDFLAGS + MYSQL_LIBS="" + else + AC_DEFINE(HAVE_MYSQL) + + AC_CHECK_FUNCS(mysql_real_query mysql_fetch_lengths) + fi + fi +else + : +fi + +AC_SUBST(MYSQL_LIBS) + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/modules/Mysql/doc/mysql b/src/modules/Mysql/doc/mysql new file mode 100644 index 0000000000000000000000000000000000000000..8e11cefe880f1606993e05bb220d75cabed3e657 --- /dev/null +++ b/src/modules/Mysql/doc/mysql @@ -0,0 +1,378 @@ +NAME + /precompiled/sql/mysql - Interface to the Mysql database (ALPHA) + +DESCRIPTION + /precompiled/sql/mysql is a pre-compiled Pike program. It enables + access to the Mysql database from within Pike. /precompiled/sql/mysql + is a part of the mysql module. + + Mysql is available from http://www.tcx.se/ . + +KEYWORDS + sql, database + + +============================================================================ +NAME + create - connect to the Mysql database + +SYNTAX + #include <mysql.h> + + object(Mysql) Mysql(); + or + object(Mysql) Mysql(string hostname); + or + object(Mysql) Mysql(string hostname, string database); + or + object(Mysql) Mysql(string hostname, string database, string user); + or + object(Mysql) Mysql(string hostname, string database, string user, + string password); + +DESCRIPTION + To access the Mysql database, you must first connect to it. This is + done with the function Mysql(). + + If you give no argument, or give "" as hostname it will connect with + a UNIX-domain socket, which is a big performance gain. + + +============================================================================ +NAME + affected_rows - return the number of affected rows in the table + +SYNTAX + #include <mysql.h> + + int mysql->affected_rows(); + +DESCRIPTION + Returns the number of affected rows. + + +============================================================================ +NAME + insert_id - return the insert id + +SYNTAX + #include <mysql.h> + + int mysql->insert_id(); + +DESCRIPTION + Returns the insert id. + + +============================================================================ +NAME + error - return the last error in Mysql + +SYNTAX + #include <mysql.h> + + string mysql->error(); + +DESCRIPTION + When a Mysql-method fails you can get a description of why with this + function. + + +============================================================================ +NAME + select_db - select database + +SYNTAX + #include <mysql.h> + + void select_db(string database); + +DESCRIPTION + The Mysql-server can hold several databases. You select which one + you want to access with this function. + +SEE ALSO + mysql->create, mysql->create_db, mysql->drop_db + + +============================================================================ +NAME + query - make an SQL query + +SYNTAX + #include <mysql.h> + + int|object(Mysql_result) mysql->query(string q); + +DESCRIPTION + This function sends an SQL query to the Mysql-server. The result + of the query is returned as a /precompiled/sql/mysql_result object. + Returns 0 if the query didn't return any result (e.g. INSERT or + similar). + +SEE ALSO + /precompiled/sql/mysql_result + + +============================================================================ +NAME + create_db - create a new database + +SYNTAX + #include <mysql.h> + + void mysql->create_db(string database); + +DESCRIPTION + This function creates a new database in the Mysql-server. + +SEE ALSO + mysql->select_db, mysql->drop_db + + +============================================================================ +NAME + drop_db - drop a database + +SYNTAX + #include <mysql.h> + + void mysql->drop_db(string database); + +DESCRIPTION + This function drops a database from a Mysql-server. + +SEE ALSO + mysql->create_db, mysql->select_db + + +============================================================================ +NAME + shutdown - shutdown the Mysql-server + +SYNTAX + #include <mysql.h> + + void mysql->shutdown(); + +DESCRIPTION + This function shuts down a running Mysql-server. + +SEE ALSO + mysql->reload + + +============================================================================ +NAME + reload - reload the tables + +SYNTAX + #include <mysql.h> + + void mysql->reload(); + +DESCRIPTION + This function causes the Mysql-server to reload its tables. + +SEE ALSO + mysql->shutdown + + +============================================================================ +NAME + statistics - some Mysql-server statistics + +SYNTAX + #include <mysql.h> + + string mysql->statistics(); + +DESCRIPTION + This function returns some server statistics. + +EXAMPLE + + #include <mysql.h> + + int main() + { + write(Mysql()->statistics()); + return(0); + } + +SEE ALSO + mysql->server_info, mysql->host_info, mysql->protocol_info + + +============================================================================ +NAME + server_info - give the version number of the Mysql-server + +SYNTAX + #include <mysql.h> + + string mysql->server_info(); + +DESCRIPTION + This function returns the version number of the Mysql-server. + +SEE ALSO + mysql->statistics, mysql->host_info, mysql->protocol_info + + +============================================================================ +NAME + host_info - give information about the Mysql-server connection + +SYNTAX + #include <mysql.h> + + string mysql->host_info(); + +DESCRIPTION + This function returns a string describing the connection to + the Mysql-server. + +SEE ALSO + mysql->statistics, mysql->server_info, mysql->protocol_info + + +============================================================================ +NAME + protocol_info - give the Mysql protocol version + +SYNTAX + #include <mysql.h> + + int mysql->protocol_info(); + +DESCRIPTION + This function returns the version number of the protocol the + Mysql-server uses. + +SEE ALSO + mysql->statistics, mysql->server_info, mysql->host_info + + +============================================================================ +NAME + list_dbs - list databases + +SYNTAX + #include <mysql.h> + + object(Mysql_result) mysql->list_dbs(); + or + object(Mysql_result) mysql->list_dbs(string wild); + +DESCRIPTION + Returns a table containing the names of all databases in the + Mysql-server. If an argument is specified, only those matching + wild are returned. + +SEE ALSO + mysql->list_tables, mysql->list_fields, mysql->list_processes, + /precompiled/sql/mysql_result + + +============================================================================ +NAME + list_tables - list tables in the current database + +SYNTAX + #include <mysql.h> + + object(Mysql_result) mysql->list_tables(); + or + object(Mysql_result) mysql->list_tables(string wild); + +DESCRIPTION + Returns a table containing the names of all tables in the current + database. If an argument is given, only those matching wild are + returned. + +SEE ALSO + mysql->list_dbs, mysql->list_fields, mysql->list_processes, + /precompiled/sql/mysql_result + + +============================================================================ +NAME + list_fields - list all fields + +SYNTAX + #include <mysql.h> + + array(int|mapping(string:mixed)) mysql->list_fields(string table); + or + array(int|mapping(string:mixed)) mysql->list_fields(string table, + string wild); + +DESCRIPTION + Returns an array of mappings with information about the fields in the + specified table. + + The mappings contain the following entries: + + "name": string The name of the field. + "table": string The name of the table. + "default": string The default value for the field. + "type": string The type of the field. + "length": int The length of the field. + "max_length": int The length of the longest element in this field. + "flags": multiset(string) Some flags. + "decimals": int The number of decimalplaces. + + The type of the field can be any of: + "decimal", "char", "short", "long", "float", "double", "null", + "time", "longlong", "int24", "tiny blob", "medium blob", + "long blob", "var string", "string" or "unknown". + + The flags multiset can contain any of + "primary_key": This field is part of the primary key for this table. + "not_null": This field may not be NULL. + "blob": This field is a blob field. + +NOTA BENE + Michael Widenius recomends usage of the following query instead: + show fields in 'table' like "wild" + +SEE ALSO + mysql->list_dbs, mysql->list_tables, mysql->list_processes, + mysql_result->fetch_fields + + +============================================================================ +NAME + list_processes - list all processes in the Mysql-server + +SYNTAX + #include <mysql.h> + + object(Mysql_result) mysql->list_processes(); + +DESCRIPTION + Returns a table containing the names of all processes in the + Mysql-server. + +SEE ALSO + mysql->list_dbs, mysql->list_tables, mysql->list_fields, + /precompiled/sql/mysql_result + + +============================================================================ +NAME + binary_data - inform if this version of mysql supports binary data + +SYNTAX + int mysql->binary_data(); + +DESCRIPTION + This function returns non-zero if binary data can be reliably stored + and retreived with this version of the mysql-module. + + Usually, there is no problem storing binary data in mysql-tables, + but data containing '\0' (NUL) couldn't be fetched with old + versions (prior to 3.20.5) of the mysql-library. + + diff --git a/src/modules/Mysql/doc/mysql_result b/src/modules/Mysql/doc/mysql_result new file mode 100644 index 0000000000000000000000000000000000000000..f62e5baa142f76ee1d89d7a964bc3fd227afea74 --- /dev/null +++ b/src/modules/Mysql/doc/mysql_result @@ -0,0 +1,204 @@ +NAME + /precompiled/sql/mysql_result - result of an Mysql query (ALPHA) + +DESCRIPTION + /precompiled/sql/mysql_result is a pre-compiled Pike program. It + contains the result of a Mysql-query. /precompiled/sql/mysql_result + is a part of the mysql module. + + Mysql is available from http://www.tcx.se/ . + +KEYWORDS + sql, database + +SEE ALSO + /precompiled/sql/mysql + + +============================================================================ +NAME + create - make a new mysql_result object + +SYNTAX + #include <mysql.h> + + object(Mysql_result) mysql->query(string q); + or + object(Mysql_result) mysql->list_dbs(); + or + object(Mysql_result) mysql->list_dbs(string wild); + or + object(Mysql_result) mysql->list_tables(); + or + object(Mysql_result) mysql->list_tables(string wild); + or + object(Mysql_result) mysql->list_processes(); + +DESCRIPTION + Creates a Mysql result table. + +SEE ALSO + mysql->query, mysql->list_dbs, mysql->list_tables, + mysql->list_processes, /precompiled/sql/mysql + + +============================================================================ +NAME + num_rows - number of rows in the result + +SYNTAX + #include <mysql.h> + + int mysql_result->num_rows(); + +DESCRIPTION + Returns the number of rows in the result. + +SEE ALSO + mysql_result->num_fields + + +============================================================================ +NAME + num_fields - number of fields in the result + +SYNTAX + #include <mysql.h> + + int mysql_result->num_fields(); + +DESCRIPTION + Returns the number of fields in the result. + +SEE ALSO + mysql_result->num_rows + + +============================================================================ +NAME + field_seek - skip to specified field (OPTIONAL) + +SYNTAX + #include <mysql.h> + + void mysql_result->field_seek(int field_no); + +DESCRIPTION + Places the field cursor at the specified position. This affects + which field mysql_result->fetch_field() will return next. + + Fields are numbered starting with 0. + +NOTA BENE + This function exists only if SUPPORT_FIELD_SEEK was defined when + compiling the mysql-module. + +SEE ALSO + mysql_result->fetch_field, mysql_result->fetch_fields + + +============================================================================ +NAME + eof - at end of result table + +SYNTAX + #include <mysql.h> + + int mysql_result->eof(); + +DESCRIPTION + Returns non-zero when all rows have been read. + +SEE ALSO + mysql_result->fetch_row + + +============================================================================ +NAME + fetch_field - return specification of current field (OPTIONAL) + +SYNTAX + #include <mysql.h> + + int|mapping(string:mixed) mysql_result->fetch_field(); + +DESCRIPTION + Returns a mapping with information about the current field, and + advances the field cursor one step. Returns 0 if there are no more + fields. + + The mapping contains the same entries as those returned by + mysql->list_fields(), except that the "default" entry is missing. + +NOTA BENE + This function exists only if SUPPORT_FIELD_SEEK was defined when + compiling the mysql-module. + +SEE ALSO + mysql_result->fetch_fields, mysql_result->field_seek, + sql->list_fields + + +============================================================================ +NAME + fetch_fields - return specification of all remaining fields + +SYNTAX + #include <mysql.h> + + array(int|mapping(string:mixed)) mysql_result->fetch_fields(); + +DESCRIPTION + Returns an array with one mapping for every remaining field in the + result table. + + It returns data similar to mysql->list_fields(), except for that + the "default" entry is missing. + +NOTA BENE + Resets the field cursor to 0. + + This function exists even if SUPPORT_FIELD_SEEK wasn't defined when + the Mysql-module was compiled. + +SEE ALSO + mysql_result->fetch_field, mysql_result->field_seek, + mysql->list_fields + + +============================================================================ +NAME + seek - skip ahead a number of rows + +SYNTAX + #include <mysql.h> + + void mysql_result->seek(int r); + +DESCRIPTION + Skips ahead the specified number of rows. + +BUGS + Can only seek forward (limitation in Mysql). + +SEE ALSO + mysql_result->fetch_row + + +============================================================================ +NAME + fetch_row - fetch the next row from the result + +SYNTAX + #include <mysql.h> + + int|array(string|int) mysql_result->fetch_row(); + +DESCRIPTION + Returns an array with the contents of the next row in the result. + Advances the row cursor to the next row. Returns 0 at end of table. + +SEE ALSO + mysql_result->seek + + diff --git a/src/modules/Mysql/mysql.c b/src/modules/Mysql/mysql.c new file mode 100644 index 0000000000000000000000000000000000000000..87f7968202cdb6813e158564660e880fdd19da6d --- /dev/null +++ b/src/modules/Mysql/mysql.c @@ -0,0 +1,706 @@ +/* + * $Id: mysql.c,v 1.1 1997/02/11 08:36:34 hubbe Exp $ + * + * SQL database functionality for Pike + * + * Henrik Grubbstr�m 1996-12-21 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_MYSQL + +/* + * Includes + */ + +/* From the mysql-dist */ +/* Workaround for versions prior to 3.20.0 not beeing protected for + * multiple inclusion. + */ +#ifndef _mysql_h +#ifdef HAVE_MYSQL_H +#include <mysql.h> +#else +#ifdef HAVE_MYSQL_MYSQL_H +#include <mysql/mysql.h> +#else +#error Need mysql.h header-file +#endif /* HAVE_MYSQL_MYSQL_H */ +#endif /* HAVE_MYSQL_H */ +#ifndef _mysql_h +#define _mysql_h +#endif +#endif + +/* dynamic_buffer.h contains a conflicting typedef for string + * we don't use any dynamic buffers, so we have this work-around + */ +#define DYNAMIC_BUFFER_H +typedef struct dynamic_buffer_s dynamic_buffer; + +#endif /* HAVE_MYSQL */ + +/* From the Pike-dist */ +#include <global.h> +#include <svalue.h> +#include <object.h> +#include <stralloc.h> +#include <interpret.h> +#include <port.h> +#include <error.h> +#include <las.h> +#include <threads.h> + +/* System includes */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +#ifdef HAVE_MYSQL + +/* Local includes */ +#include "precompiled_mysql.h" + +/* + * Globals + */ + +RCSID("$Id: mysql.c,v 1.1 1997/02/11 08:36:34 hubbe Exp $"); + +struct program *mysql_program = NULL; + +/* + * Functions + */ + +/* + * State maintenance + */ + +static void init_mysql_struct(struct object *o) +{ + MEMSET(PIKE_MYSQL, 0, sizeof(struct precompiled_mysql)); +} + +static void exit_mysql_struct(struct object *o) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *last_result = PIKE_MYSQL->last_result; + + PIKE_MYSQL->last_result = NULL; + PIKE_MYSQL->socket = NULL; + + THREADS_ALLOW(); + + if (last_result) { + mysql_free_result(last_result); + } + if (socket) { + mysql_close(socket); + } + + THREADS_DISALLOW(); +} + +/* + * Methods + */ + +/* void create(string|void host, string|void database, string|void user, string|void password) */ +static void f_create(INT32 args) +{ + MYSQL *mysql = &PIKE_MYSQL->mysql; + MYSQL *socket; + char *host = NULL; + char *database = NULL; + char *user = NULL; + char *password = NULL; + + if (args >= 1) { + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql()\n"); + } + if (sp[-args].u.string->len) { + host = sp[-args].u.string->str; + } + + if (args >= 2) { + if (sp[1-args].type != T_STRING) { + error("Bad argument 2 to mysql()\n"); + } + if (sp[1-args].u.string->len) { + database = sp[1-args].u.string->str; + } + + if (args >= 3) { + if (sp[2-args].type != T_STRING) { + error("Bad argument 3 to mysql()\n"); + } + if (sp[2-args].u.string->len) { + user = sp[2-args].u.string->str; + } + + if (args >= 4) { + if (sp[3-args].type != T_STRING) { + error("Bad argument 4 to mysql()\n"); + } + if (sp[3-args].u.string->len) { + password = sp[3-args].u.string->str; + } + } + } + } + } + + THREADS_ALLOW(); + + socket = mysql_connect(mysql, host, user, password); + + THREADS_DISALLOW(); + + if (!(PIKE_MYSQL->socket = socket)) { + error("mysql(): Couldn't connect to SQL-server\n"); + } + if (database) { + int tmp; + + THREADS_ALLOW(); + + tmp = mysql_select_db(socket, database); + + THREADS_DISALLOW(); + + if (tmp < 0) { + mysql_close(PIKE_MYSQL->socket); + PIKE_MYSQL->socket = NULL; + error("mysql(): Couldn't select database \"%s\"\n", database); + } + } + + pop_n_elems(args); +} + +/* int affected_rows() */ +static void f_affected_rows(INT32 args) +{ + pop_n_elems(args); + push_int(mysql_affected_rows(PIKE_MYSQL->socket)); +} + +/* int insert_id() */ +static void f_insert_id(INT32 args) +{ + pop_n_elems(args); + push_int(mysql_insert_id(PIKE_MYSQL->socket)); +} + +/* int|string error() */ +static void f_error(INT32 args) +{ + char *error_msg = mysql_error(PIKE_MYSQL->socket); + + pop_n_elems(args); + if (error_msg && *error_msg) { + push_text(error_msg); + } else { + push_int(0); + } +} + +/* void select_db(string database) */ +static void f_select_db(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + char *database; + int tmp; + + if (!args) { + error("Too few arguments to mysql->select_db()\n"); + } + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->select_db()\n"); + } + + database = sp[-args].u.string->str; + + THREADS_ALLOW(); + + tmp = mysql_select_db(socket, database); + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->select_db(): Couldn't select database \"%s\"\n", + sp[-args].u.string->str); + } + + pop_n_elems(args); +} + +/* object(mysql_result) big_query(string q) */ +static void f_big_query(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *result; + char *query; + int qlen; + int tmp; + + if (!args) { + error("Too few arguments to mysql->big_query()\n"); + } + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->big_query()\n"); + } + + query = sp[-args].u.string->str; + qlen = sp[-args].u.string->len; + + THREADS_ALLOW(); + + /* NOTE the temporary tmp is needed since THREADS_ALLOW() opens a scope, + * which is closed at THREADS_DISALLOW() + */ + +#ifdef HAVE_MYSQL_REAL_QUERY + tmp = mysql_real_query(socket, query, qlen); +#else + tmp = mysql_query(socket, query); +#endif /* HAVE_MYSQL_REAL_QUERY */ + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->big_query(): Query \"%s\" failed\n", + sp[-args].u.string->str); + } + + THREADS_ALLOW(); + + /* The same thing applies here */ + + result = mysql_store_result(socket); + + THREADS_DISALLOW(); + + pop_n_elems(args); + + if (!(PIKE_MYSQL->last_result = result)) { + if (mysql_num_fields(socket) && mysql_error(socket)[0]) { + error("mysql->big_query(): Couldn't create result for query\n"); + } + /* query was INSERT or similar - return 0 */ + + push_int(0); + } else { + /* Return the result-object */ + + push_object(fp->current_object); + fp->current_object->refs++; + + push_object(clone(mysql_result_program, 1)); + } +} + +/* void create_db(string database) */ +static void f_create_db(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + char *database; + int tmp; + + if (!args) { + error("Too few arguments to mysql->create_db()\n"); + } + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->create_db()\n"); + } + if (sp[-args].u.string->len > 127) { + error("Database name \"%s\" is too long (max 127 characters)\n", + sp[-args].u.string->str); + } + database = sp[-args].u.string->str; + + THREADS_ALLOW(); + + tmp = mysql_create_db(socket, database); + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->create_db(): Creation of database \"%s\" failed\n", + sp[-args].u.string->str); + } + + pop_n_elems(args); +} + +/* void drop_db(string database) */ +static void f_drop_db(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + char *database; + int tmp; + + if (!args) { + error("Too few arguments to mysql->drop_db()\n"); + } + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->drop_db()\n"); + } + if (sp[-args].u.string->len > 127) { + error("Database name \"%s\" is too long (max 127 characters)\n", + sp[-args].u.string->str); + } + database = sp[-args].u.string->str; + + THREADS_ALLOW(); + + tmp = mysql_drop_db(socket, database); + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->drop_db(): Drop of database \"%s\" failed\n", + sp[-args].u.string->str); + } + + pop_n_elems(args); +} + +/* void shutdown() */ +static void f_shutdown(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + int tmp; + + THREADS_ALLOW(); + + tmp = mysql_shutdown(socket); + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->shutdown(): Shutdown failed\n"); + } + + pop_n_elems(args); +} + +/* void reload() */ +static void f_reload(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + int tmp; + + THREADS_ALLOW(); + + tmp = mysql_reload(socket); + + THREADS_DISALLOW(); + + if (tmp < 0) { + error("mysql->reload(): Reload failed\n"); + } + + pop_n_elems(args); +} + +/* string statistics() */ +static void f_statistics(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + char *stats; + + pop_n_elems(args); + + THREADS_ALLOW(); + + stats = mysql_stat(socket); + + THREADS_DISALLOW(); + + push_text(stats); +} + +/* string server_info() */ +static void f_server_info(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + char *info; + + pop_n_elems(args); + + push_text("mysql/"); + + THREADS_ALLOW(); + + info = mysql_get_server_info(socket); + + THREADS_DISALLOW(); + + push_text(info); + f_add(2); +} + +/* string host_info() */ +static void f_host_info(INT32 args) +{ + pop_n_elems(args); + + push_text(mysql_get_host_info(PIKE_MYSQL->socket)); +} + +/* int protocol_info() */ +static void f_protocol_info(INT32 args) +{ + pop_n_elems(args); + + push_int(mysql_get_proto_info(PIKE_MYSQL->socket)); +} + +/* object(mysql_res) list_dbs(void|string wild) */ +static void f_list_dbs(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *result; + char *wild = NULL; + + if (args) { + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->list_dbs()\n"); + } + if (sp[-args].u.string->len > 80) { + error("Wildcard \"%s\" is too long (max 80 characters)\n", + sp[-args].u.string->str); + } + wild = sp[-args].u.string->str; + } + + THREADS_ALLOW(); + + result = mysql_list_dbs(socket, wild); + + THREADS_DISALLOW(); + + if (!(PIKE_MYSQL->last_result = result)) { + error("mysql->list_dbs(): Cannot list databases: %s\n", + mysql_error(PIKE_MYSQL->socket)); + } + + pop_n_elems(args); + + push_object(fp->current_object); + fp->current_object->refs++; + + push_object(clone(mysql_result_program, 1)); +} + +/* object(mysql_res) list_tables(void|string wild) */ +static void f_list_tables(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *result; + char *wild = NULL; + + if (args) { + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->list_tables()\n"); + } + if (sp[-args].u.string->len > 80) { + error("Wildcard \"%s\" is too long (max 80 characters)\n", + sp[-args].u.string->str); + } + wild = sp[-args].u.string->str; + } + + THREADS_ALLOW(); + + result = mysql_list_tables(socket, wild); + + THREADS_DISALLOW(); + + if (!(PIKE_MYSQL->last_result = result)) { + error("mysql->list_tables(): Cannot list databases: %s\n", + mysql_error(PIKE_MYSQL->socket)); + } + + pop_n_elems(args); + + push_object(fp->current_object); + fp->current_object->refs++; + + push_object(clone(mysql_result_program, 1)); +} + +/* array(int|mapping(string:mixed)) list_fields(string table, void|string wild) */ +static void f_list_fields(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *result; + MYSQL_FIELD *field; + int i = 0; + char *table; + char *wild = NULL; + + if (!args) { + error("Too few arguments to mysql->list_fields()\n"); + } + + if (sp[-args].type != T_STRING) { + error("Bad argument 1 to mysql->list_fields()\n"); + } + if (sp[-args].u.string->len > 125) { + error("Table name \"%s\" is too long (max 125 characters)\n", + sp[-args].u.string->str); + } + table = sp[-args].u.string->str; + if (args > 1) { + if (sp[-args+1].type != T_STRING) { + error("Bad argument 2 to mysql->list_fields()\n"); + } + if (sp[-args+1].u.string->len + sp[-args].u.string->len > 125) { + error("Wildcard \"%s\" + table name \"%s\" is too long " + "(max 125 characters)\n", + sp[-args+1].u.string->str, sp[-args].u.string->str); + } + wild = sp[-args+1].u.string->str; + } + + THREADS_ALLOW(); + + result = mysql_list_fields(socket, table, wild); + + THREADS_DISALLOW(); + + if (!result) { + error("mysql->list_fields(): Cannot list databases: %s\n", + mysql_error(PIKE_MYSQL->socket)); + } + + pop_n_elems(args); + + while ((field = mysql_fetch_field(result))) { + mysqlmod_parse_field(field, 1); + i++; + } + f_aggregate(i); +} + +/* object(mysql_res) list_processes() */ +static void f_list_processes(INT32 args) +{ + MYSQL *socket = PIKE_MYSQL->socket; + MYSQL_RES *result; + + pop_n_elems(args); + + THREADS_ALLOW(); + + result = mysql_list_processes(socket); + + THREADS_DISALLOW(); + + if (!(PIKE_MYSQL->last_result = result)) { + error("mysql->list_processes(): Cannot list databases: %s\n", + mysql_error(PIKE_MYSQL->socket)); + } + + push_object(fp->current_object); + fp->current_object->refs++; + + push_object(clone(mysql_result_program, 1)); +} + +/* + * Support for binary data in tables + */ +static void f_binary_data(INT32 args) +{ + pop_n_elems(args); +#ifdef HAVE_MYSQL_FETCH_LENGTHS + push_int(1); +#else + push_int(0); +#endif /* HAVE_MYSQL_FETCH_LENGTHS */ +} + +#endif /* HAVE_MYSQL */ + +/* + * Module linkage + */ + + +void pike_module_init(void) +{ +#ifdef HAVE_MYSQL + /* + * start_new_program(); + * + * add_storage(); + * + * add_function(); + * add_function(); + * ... + * + * set_init_callback(); + * set_exit_callback(); + * + * program = end_c_program(); + * program->refs++; + * + */ + + start_new_program(); + add_storage(sizeof(struct precompiled_mysql)); + + add_function("error", f_error, "function(void:int|string)", OPT_EXTERNAL_DEPEND); + add_function("create", f_create, "function(string|void, string|void, string|void, string|void:void)", OPT_SIDE_EFFECT); + add_function("affected_rows", f_affected_rows, "function(void:int)", OPT_EXTERNAL_DEPEND); + add_function("insert_id", f_insert_id, "function(void:int)", OPT_EXTERNAL_DEPEND); + add_function("select_db", f_select_db, "function(string:void)", OPT_SIDE_EFFECT); + add_function("big_query", f_big_query, "function(string:int|object)", OPT_EXTERNAL_DEPEND); + add_function("create_db", f_create_db, "function(string:void)", OPT_SIDE_EFFECT); + add_function("drop_db", f_drop_db, "function(string:void)", OPT_SIDE_EFFECT); + add_function("shutdown", f_shutdown, "function(void:void)", OPT_SIDE_EFFECT); + add_function("reload", f_reload, "function(void:void)", OPT_SIDE_EFFECT); + add_function("statistics", f_statistics, "function(void:string)", OPT_EXTERNAL_DEPEND); + add_function("server_info", f_server_info, "function(void:string)", OPT_EXTERNAL_DEPEND); + add_function("host_info", f_host_info, "function(void:string)", OPT_EXTERNAL_DEPEND); + add_function("protocol_info", f_protocol_info, "function(void:int)", OPT_EXTERNAL_DEPEND); + add_function("list_dbs", f_list_dbs, "function(void|string:object)", OPT_EXTERNAL_DEPEND); + add_function("list_tables", f_list_tables, "function(void|string:object)", OPT_EXTERNAL_DEPEND); + add_function("list_fields", f_list_fields, "function(string, void|string:array(int|mapping(string:mixed)))", OPT_EXTERNAL_DEPEND); + add_function("list_processes", f_list_processes, "function(void|string:object)", OPT_EXTERNAL_DEPEND); + + add_function("binary_data", f_binary_data, "function(void:int)", OPT_TRY_OPTIMIZE); + + set_init_callback(init_mysql_struct); + set_exit_callback(exit_mysql_struct); + + mysql_program = end_program(); + add_program_constant("mysql", mysql_program, 0); + + init_mysql_res_programs(); +#endif /* HAVE_MYSQL */ +} + +void pike_module_exit(void) +{ +#ifdef HAVE_MYSQL + exit_mysql_res(); + + if (mysql_program) { + free_program(mysql_program); + mysql_program = NULL; + } +#endif /* HAVE_MYSQL */ +} + diff --git a/src/modules/Mysql/precompiled_mysql.h b/src/modules/Mysql/precompiled_mysql.h new file mode 100644 index 0000000000000000000000000000000000000000..e56e403586bcb34b21b31b234768f263ac19c84b --- /dev/null +++ b/src/modules/Mysql/precompiled_mysql.h @@ -0,0 +1,78 @@ +/* + * $Id: precompiled_mysql.h,v 1.1 1997/02/11 08:36:35 hubbe Exp $ + * + * SQL database connectivity for Pike + * + * Henrik Grubbstr�m 1996-12-21 + */ + +#ifndef PRECOMPILED_MYSQL_H +#define PRECOMPILED_MYSQL_H + +/* + * Includes + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +/* From the mysql-dist */ +/* Workaround for versions prior to 3.20.0 not beeing protected for + * multiple inclusion. + */ +#ifndef _mysql_h +#ifdef HAVE_MYSQL_H +#include <mysql.h> +#else +#ifdef HAVE_MYSQL_MYSQL_H +#include <mysql/mysql.h> +#else +#error Need mysql.h header-file +#endif /* HAVE_MYSQL_MYSQL_H */ +#endif /* HAVE_MYSQL_H */ +#ifndef _mysql_h +#define _mysql_h +#endif +#endif + +/* From the Pike-dist */ + +/* + * Structures + */ + +struct precompiled_mysql { + MYSQL mysql, *socket; + MYSQL_RES *last_result; /* UGLY way to pass arguments to create() */ +}; + +struct precompiled_mysql_result { + MYSQL_RES *result; +}; + +/* + * Defines + */ + +#define PIKE_MYSQL ((struct precompiled_mysql *)(fp->current_storage)) +#define PIKE_MYSQL_RES ((struct precompiled_mysql_result *)(fp->current_storage)) + +/* + * Globals + */ + +extern struct program *mysql_program; +extern struct program *mysql_result_program; + +/* + * Prototypes + */ + +/* From result.c */ + +void init_mysql_res_efuns(void); +void init_mysql_res_programs(void); +void exit_mysql_res(void); + +#endif /* PRECOMPILED_MYSQL_H */ diff --git a/src/modules/Mysql/result.c b/src/modules/Mysql/result.c new file mode 100644 index 0000000000000000000000000000000000000000..6e9f6d9a9dd93bdd219547a24f2453e84af6fea6 --- /dev/null +++ b/src/modules/Mysql/result.c @@ -0,0 +1,449 @@ +/* + * $Id: result.c,v 1.1 1997/02/11 08:36:35 hubbe Exp $ + * + * mysql query result + * + * Henrik Grubbstr�m 1996-12-21 + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_MYSQL +/* + * Includes + */ + +/* From the mysql-dist */ +/* Workaround for versions prior to 3.20.0 not beeing protected for + * multiple inclusion. + */ +#ifndef _mysql_h +#ifdef HAVE_MYSQL_H +#include <mysql.h> +#else +#ifdef HAVE_MYSQL_MYSQL_H +#include <mysql/mysql.h> +#else +#error Need mysql.h header-file +#endif /* HAVE_MYSQL_MYSQL_H */ +#endif /* HAVE_MYSQL_H */ +#ifndef _mysql_h +#define _mysql_h +#endif +#endif + +/* dynamic_buffer.h contains a conflicting typedef for string + * we don't use any dynamic buffers, so we have this work-around + */ +#define DYNAMIC_BUFFER_H +typedef struct dynamic_buffer_s dynamic_buffer; + +/* From the Pike-dist */ +#include <global.h> +#include <svalue.h> +#include <mapping.h> +#include <object.h> +#include <program.h> +#include <stralloc.h> +#include <interpret.h> +#include <error.h> +#include <builtin_functions.h> +#include <las.h> +#include <threads.h> + +/* Local includes */ +#include "precompiled_mysql.h" + +/* System includes */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +/* Define this to get support for field->default. NOT SUPPORTED */ +#undef SUPPORT_DEFAULT + +/* Define this to get field_seek() and fetch_field() */ +/* #define SUPPORT_FIELD_SEEK */ + +/* + * Globals + */ + +RCSID("$Id: result.c,v 1.1 1997/02/11 08:36:35 hubbe Exp $"); + +struct program *mysql_result_program = NULL; + +/* + * Functions + */ + +/* + * State maintenance + */ + +static void init_res_struct(struct object *o) +{ + memset(PIKE_MYSQL_RES, 0, sizeof(struct precompiled_mysql_result)); +} + +static void exit_res_struct(struct object *o) +{ + if (PIKE_MYSQL_RES->result) { + mysql_free_result(PIKE_MYSQL_RES->result); + PIKE_MYSQL_RES->result = NULL; + } +} + +/* + * Help functions + */ + +void mysqlmod_parse_field(MYSQL_FIELD *field, int support_default) +{ + if (field) { + int nbits = 0; + push_text("name"); push_text(field->name); + push_text("table"); push_text(field->table); + if (support_default) { + push_text("default"); + if (field->def) { + push_text(field->def); + } else { + push_int(0); + } + } + push_text("type"); + switch(field->type) { + case FIELD_TYPE_DECIMAL: + push_text("decimal"); + break; + case FIELD_TYPE_CHAR: + push_text("char"); + break; + case FIELD_TYPE_SHORT: + push_text("short"); + break; + case FIELD_TYPE_LONG: + push_text("long"); + break; + case FIELD_TYPE_FLOAT: + push_text("float"); + break; + case FIELD_TYPE_DOUBLE: + push_text("double"); + break; + case FIELD_TYPE_NULL: + push_text("null"); + break; + case FIELD_TYPE_TIME: + push_text("time"); + break; + case FIELD_TYPE_LONGLONG: + push_text("longlong"); + break; + case FIELD_TYPE_INT24: + push_text("int24"); + break; + case FIELD_TYPE_TINY_BLOB: + push_text("tiny blob"); + break; + case FIELD_TYPE_MEDIUM_BLOB: + push_text("medium blob"); + break; + case FIELD_TYPE_LONG_BLOB: + push_text("long blob"); + break; + case FIELD_TYPE_BLOB: + push_text("blob"); + break; + case FIELD_TYPE_VAR_STRING: + push_text("var string"); + break; + case FIELD_TYPE_STRING: + push_text("string"); + break; + default: + push_text("unknown"); + break; + } + push_text("length"); push_int(field->length); + push_text("max_length"); push_int(field->max_length); + + push_text("flags"); + if (IS_PRI_KEY(field->flags)) { + nbits++; + push_text("primary_key"); + } + if (IS_NOT_NULL(field->flags)) { + nbits++; + push_text("not_null"); + } + if (IS_BLOB(field->flags)) { + nbits++; + push_text("blob"); + } + f_aggregate_multiset(nbits); + + push_text("decimals"); push_int(field->decimals); + + if (support_default) { + f_aggregate_mapping(8*2); + } else { + f_aggregate_mapping(7*2); + } + } else { + /* + * Should this be an error? + */ + push_int(0); + } +} + +/* + * Methods + */ + +/* void create(object(mysql)) */ +static void f_create(INT32 args) +{ + if (!args) { + error("Too few arguments to mysql_result()\n"); + } + if ((sp[-args].type != T_OBJECT) || + (sp[-args].u.object->prog != mysql_program)) { + error("Bad argument 1 to mysql_result()\n"); + } + + PIKE_MYSQL_RES->result = ((struct precompiled_mysql *)sp[-args].u.object->storage)->last_result; + ((struct precompiled_mysql *)sp[-args].u.object->storage)->last_result = NULL; + + pop_n_elems(args); + + if (!PIKE_MYSQL_RES->result) { + error("mysql_result(): No result to clone\n"); + } +} + +/* int num_rows() */ +static void f_num_rows(INT32 args) +{ + pop_n_elems(args); + push_int(mysql_num_rows(PIKE_MYSQL_RES->result)); +} + +/* int num_fields() */ +static void f_num_fields(INT32 args) +{ + pop_n_elems(args); + push_int(mysql_num_fields(PIKE_MYSQL_RES->result)); +} + +#ifdef SUPPORT_FIELD_SEEK + +/* void field_seek(int fieldno) */ +static void f_field_seek(INT32 args) +{ + if (!args) { + error("Too few arguments to mysql->field_seek()\n"); + } + if (sp[-args].type != T_INT) { + error("Bad argument 1 to mysql->field_seek()\n"); + } + mysql_field_seek(PIKE_MYSQL_RES->result, sp[-args].u.integer); + pop_n_elems(args); +} + +#endif /* SUPPORT_FIELD_SEEK */ + +/* int eof() */ +static void f_eof(INT32 args) +{ + pop_n_elems(args); + push_int(mysql_eof(PIKE_MYSQL_RES->result)); +} + +#ifdef SUPPORT_FIELD_SEEK + +/* int|mapping(string:mixed) fetch_field() */ +static void f_fetch_field(INT32 args) +{ + MYSQL_FIELD *field; + MYSQL_RES *res = PIKE_MYSQL_RES->result; + + pop_n_elems(args); + + THREADS_ALLOW(); + + field = mysql_fetch_field(res); + + THREADS_DISALLOW(); + + mysqlmod_parse_field(field, 0); +} + +#endif /* SUPPORT_FIELD_SEEK */ + +/* array(int|mapping(string:mixed)) fetch_fields() */ +static void f_fetch_fields(INT32 args) +{ + MYSQL_FIELD *field; + int i = 0; + + pop_n_elems(args); + + while ((field = mysql_fetch_field(PIKE_MYSQL_RES->result))) { + mysqlmod_parse_field(field, 0); + i++; + } + f_aggregate(i); + + mysql_field_seek(PIKE_MYSQL_RES->result, 0); +} + +/* void seek(int row) */ +static void f_seek(INT32 args) +{ + if (!args) { + error("Too few arguments to mysql_result->seek()\n"); + } + if (sp[-args].type != T_INT) { + error("Bad argument 1 to mysql_result->seek()\n"); + } + if (sp[-args].u.integer < 0) { + error("Negative argument 1 to mysql_result->seek()\n"); + } + mysql_data_seek(PIKE_MYSQL_RES->result, sp[-args].u.integer); + + pop_n_elems(args); +} + +/* int|array(string|float|int) fetch_row() */ +static void f_fetch_row(INT32 args) +{ + int num_fields = mysql_num_fields(PIKE_MYSQL_RES->result); + MYSQL_ROW row = mysql_fetch_row(PIKE_MYSQL_RES->result); +#ifdef HAVE_MYSQL_FETCH_LENGTHS + int *row_lengths = mysql_fetch_lengths(PIKE_MYSQL_RES->result); +#endif /* HAVE_MYSQL_FETCH_LENGTHS */ + + pop_n_elems(args); + + mysql_field_seek(PIKE_MYSQL_RES->result, 0); + + if ((num_fields > 0) && row) { + int i; + + for (i=0; i < num_fields; i++) { + if (row[i]) { + MYSQL_FIELD *field; + + if (field = mysql_fetch_field(PIKE_MYSQL_RES->result)) { + switch (field->type) { + /* Integer types */ + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + case FIELD_TYPE_INT24: +#if 0 + /* This one will not always fit in an INT32 */ + case FIELD_TYPE_LONGLONG: +#endif /* 0 */ + push_int(atoi(row[i])); + break; + /* Floating point types */ + case FIELD_TYPE_DECIMAL: /* Is this a float or an int? */ + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + push_float(atof(row[i])); + break; + default: +#ifdef HAVE_MYSQL_FETCH_LENGTHS + push_string(make_shared_binary_string(row[i], row_lengths[i])); +#else + push_text(row[i]); +#endif /* HAVE_MYSQL_FETCH_LENGTHS */ + break; + } + } else { + /* Probably doesn't happen, but... */ +#ifdef HAVE_MYSQL_FETCH_LENGTHS + push_string(make_shared_binary_string(row[i], row_lengths[i])); +#else + push_text(row[i]); +#endif /* HAVE_MYSQL_FETCH_LENGTHS */ + } + } else { + /* NULL? */ + push_int(0); + } + } + f_aggregate(num_fields); + } else { + /* No rows left in result */ + push_int(0); + } + + mysql_field_seek(PIKE_MYSQL_RES->result, 0); +} + +/* + * Module linkage + */ + + +void init_mysql_res_programs(void) +{ + /* + * start_new_program(); + * + * add_storage(); + * + * add_function(); + * add_function(); + * ... + * + * set_init_callback(); + * set_exit_callback(); + * + * program = end_c_program(); + * program->refs++; + * + */ + + start_new_program(); + add_storage(sizeof(struct precompiled_mysql_result)); + + add_function("create", f_create, "function(object:void)", OPT_SIDE_EFFECT); + add_function("num_rows", f_num_rows, "function(void:int)", OPT_EXTERNAL_DEPEND); + add_function("num_fields", f_num_fields, "function(void:int)", OPT_EXTERNAL_DEPEND); +#ifdef SUPPORT_FIELD_SEEK + add_function("field_seek", f_field_seek, "function(int:void)", OPT_SIDE_EFFECT); +#endif /* SUPPORT_FIELD_SEEK */ + add_function("eof", f_eof, "function(void:int)", OPT_EXTERNAL_DEPEND); +#ifdef SUPPORT_FIELD_SEEK + add_function("fetch_field", f_fetch_field, "function(void:int|mapping(string:mixed))", OPT_EXTERNAL_DEPEND); +#endif /* SUPPORT_FIELD_SEEK */ + add_function("fetch_fields", f_fetch_fields, "function(void:array(int|mapping(string:mixed)))", OPT_EXTERNAL_DEPEND); + add_function("seek", f_seek, "function(int:void)", OPT_SIDE_EFFECT); + add_function("fetch_row", f_fetch_row, "function(void:int|array(string|int|float))", OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT); + + set_init_callback(init_res_struct); + set_exit_callback(exit_res_struct); + + mysql_result_program = end_program(); + add_program_constant("mysql_result",mysql_result_program, 0); +} + +void exit_mysql_res(void) +{ + if (mysql_result_program) { + free_program(mysql_result_program); + mysql_result_program = NULL; + } +} + +#endif /* HAVE_MYSQL */ diff --git a/src/modules/Pipe/.cvsignore b/src/modules/Pipe/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..47c84b07459ebcb78e36ee8c2e1a30cee7f440ff --- /dev/null +++ b/src/modules/Pipe/.cvsignore @@ -0,0 +1,8 @@ +.pure +Makefile +config.h +config.log +config.status +configure +dependencies +stamp-h diff --git a/src/modules/Pipe/.gitignore b/src/modules/Pipe/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b27587e1b501d0baa775ec479e1a69e7da31aef0 --- /dev/null +++ b/src/modules/Pipe/.gitignore @@ -0,0 +1,8 @@ +/.pure +/Makefile +/config.h +/config.log +/config.status +/configure +/dependencies +/stamp-h diff --git a/src/modules/Pipe/Makefile.in b/src/modules/Pipe/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..2f114663c728caf7b6e11bb301fe8a705f77db0e --- /dev/null +++ b/src/modules/Pipe/Makefile.in @@ -0,0 +1,6 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=pipe.o + +@dynamic_module_makefile@ +@dependencies@ diff --git a/src/modules/Pipe/config.h.in b/src/modules/Pipe/config.h.in new file mode 100644 index 0000000000000000000000000000000000000000..508a935ac246d6d24a59b2a5c7117feb8fc9ef50 --- /dev/null +++ b/src/modules/Pipe/config.h.in @@ -0,0 +1,13 @@ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if you have the mmap function. */ +#undef HAVE_MMAP + +/* Define if you have the munmap function. */ +#undef HAVE_MUNMAP + +/* Define if you have the <linux/mman.h> header file. */ +#undef HAVE_LINUX_MMAN_H + +/* Define if you have the <sys/mman.h> header file. */ +#undef HAVE_SYS_MMAN_H diff --git a/src/modules/Pipe/configure.in b/src/modules/Pipe/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..84dd753063f827919780d8d80eb1b3967a264fdf --- /dev/null +++ b/src/modules/Pipe/configure.in @@ -0,0 +1,11 @@ +AC_INIT(pipe.c) +AC_CONFIG_HEADER(config.h) + +sinclude(../module_configure.in) + +AC_HAVE_HEADERS(sys/mman.h linux/mman.h) +AC_HAVE_FUNCS(mmap munmap) + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) + + diff --git a/src/modules/Pipe/pipe.c b/src/modules/Pipe/pipe.c new file mode 100644 index 0000000000000000000000000000000000000000..3158285191ab98ad50ed1f7257b4d792a6caf701 --- /dev/null +++ b/src/modules/Pipe/pipe.c @@ -0,0 +1,1142 @@ +#include <config.h> +#include "machine.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#else +#ifdef HAVE_LINUX_MMAN_H +#include <linux/mman.h> +#else +#ifdef HAVE_MMAP +/* sys/mman.h is _probably_ there anyway. */ +#include <sys/mman.h> +#endif +#endif +#endif + +#include <fcntl.h> + +#include "global.h" +RCSID("$Id: pipe.c,v 1.1 1997/02/11 08:38:08 hubbe Exp $"); + +#include "stralloc.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "error.h" + +#ifndef S_ISREG +#ifdef S_IFREG +#define S_ISREG(mode) (((mode) & (S_IFMT)) == (S_IFREG)) +#else +#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) +#endif +#endif + +/* +#define PIPE_STRING_DEBUG +#define PIPE_MMAP_DEBUG +#define PIPE_FILE_DEBUG +#define BLOCKING_CLOSE +*/ + +#if 0 +#define INSISTANT_WRITE +#endif + +#ifndef MAP_FILE +# define MAP_FILE 0 +#endif + +#define READ_BUFFER_SIZE 32768 +#define MAX_BYTES_IN_BUFFER 65536 + +/* + * usage: + * single socket output + * or regular file output and (multiple, adding) socket output + * with no mmap input + * + * multiple socket output without regular file output illegal + */ + +static struct program *pipe_program, *output_program; + +#define THIS ((struct pipe *)(fp->current_storage)) +#define THISOBJ (fp->current_object) + +struct input +{ + enum { I_NONE,I_OBJ,I_STRING,I_MMAP } type; + union + { + struct object *obj; + struct pike_string *str; + char *mmap; + } u; + unsigned long len; /* current input: string or mmap len */ + int set_blocking_offset, set_nonblocking_offset; + struct input *next; +}; + +struct output +{ + struct object *obj; + int write_offset, set_blocking_offset, set_nonblocking_offset; + int fd; + + enum + { + O_RUN, /* waiting for callback */ + O_SLEEP /* sleeping; waiting for more data */ + } mode; + + unsigned long pos; /* position in buffer */ + struct object *next; + struct pipe *the_pipe; +}; + +struct buffer +{ + struct pike_string *s; + struct buffer *next; +}; + +struct pipe +{ + int living_outputs; /* number of output objects */ + + struct svalue done_callback; + struct svalue output_closed_callback; + struct svalue id; + + /* + * if fd is -1: use fd + * else firstinput's type is I_MMAP: use firstinput's mmap + * else use buffer + */ + + int fd; /* buffer fd or -1 */ + + unsigned long bytes_in_buffer; + unsigned long pos; + /* fd: size of buffer file */ + /* current position of first element (buffer or mmap) */ + struct buffer *firstbuffer,*lastbuffer; + short sleeping; /* sleeping; buffer is full */ + short done; + struct input *firstinput,*lastinput; + struct object *firstoutput; +}; + +static int offset_input_read_callback; +static int offset_input_close_callback; +static int offset_output_write_callback; +static int offset_output_close_callback; +static int mmapped, nobjects, nstrings, noutputs, ninputs, nbuffers, sbuffers; + +static char static_buffer[READ_BUFFER_SIZE]; + +void close_and_free_everything(struct object *o,struct pipe *); +static INLINE void output_finish(struct object *obj); +static INLINE void output_try_write_some(struct object *obj); + +/********** internal ********************************************************/ + +/* Push a callback to this object given the internal function number. + */ +static void push_callback(int no) +{ + sp->u.object=THISOBJ; + THISOBJ->refs++; + sp->subtype=no+fp->context.identifier_level; + sp->type=T_FUNCTION; + sp++; +} + +/* Allocate a new struct input, link it last in the linked list */ +static INLINE struct input *new_input(void) +{ + struct input *i; + ninputs++; + i=ALLOC_STRUCT(input); + i->type=I_NONE; + i->next=NULL; + if (THIS->lastinput) + THIS->lastinput->next=i; + else + THIS->firstinput=i; + THIS->lastinput=i; + return i; +} + +/* Free an input struct and all that it stands for */ +static INLINE void free_input(struct input *i) +{ + ninputs--; + switch (i->type) + { + case I_OBJ: + if (!i->u.obj) break; + if (i->u.obj->prog) + { +#ifdef BLOCKING_CLOSE + apply_low(i->u.obj,i->set_blocking_offset,0); + pop_stack(); +#endif + apply(i->u.obj,"close",0); + pop_stack(); + destruct(i->u.obj); + } + free_object(i->u.obj); + nobjects--; + i->u.obj=0; + break; + + case I_STRING: + free_string(i->u.str); + nstrings--; + break; + + case I_MMAP: +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + munmap(i->u.mmap,i->len); + mmapped -= i->len; +#else + error("I_MMAP input when MMAP is diabled!"); +#endif + break; + } + free((char *)i); +} + +/* do the done_callback, then close and free everything */ +static INLINE void pipe_done(void) +{ + if (THIS->done_callback.type!=T_INT) + { + assign_svalue_no_free(sp++,&THIS->id); + apply_svalue(&(THIS->done_callback),1); + pop_stack(); + + if(!THISOBJ->prog) /* We will not free anything in this case. */ + return; + /* error("Pipe done callback destructed pipe.\n"); */ + } + close_and_free_everything(THISOBJ,THIS); +} + +static void finished_p(void) +{ + if(THIS->done) return; + + if(THIS->fd != -1) + { + if(THIS->living_outputs > 1) return; + if(THIS->firstinput) return; + + }else{ + if(THIS->living_outputs) return; + } + pipe_done(); +} + +/* Allocate a new buffer and put it at the end of the chain of buffers + * scheduled for output. Return 1 if we have more bytes in buffers + * than allowed afterwards. + */ +static INLINE int append_buffer(struct pike_string *s) + /* 1=buffer full */ +{ + struct buffer *b; + + if(THIS->fd!= -1) + { + lseek(THIS->fd,THIS->pos,0); + write(THIS->fd,s->str,s->len); + THIS->pos+=s->len; + return 0; + } + else + { + nbuffers++; + b=ALLOC_STRUCT(buffer); + b->next=NULL; + b->s=s; + sbuffers += s->len; + s->refs++; + + if (THIS->lastbuffer) + THIS->lastbuffer->next=b; + else + THIS->firstbuffer=b; + + THIS->lastbuffer=b; + + THIS->bytes_in_buffer+=s->len; + } + return THIS->bytes_in_buffer > MAX_BYTES_IN_BUFFER; +} + +/* Wake up the sleepers */ +static void low_start() +{ + struct object *obj, *next; + struct output *o; + + + THISOBJ->refs++; /* dont kill yourself now */ + for(obj=THIS->firstoutput;obj;obj=next) + { + obj->refs++; /* Hang on PLEASE!! /hubbe */ + o=(struct output *)(obj->storage); + if (o->obj && o->mode==O_SLEEP) + { + if (!o->obj->prog) + { + output_finish(obj); + } + else + { +#if 0 + push_int(0); + push_callback(offset_output_write_callback); + push_callback(offset_output_close_callback); + apply_low(o->obj,o->set_nonblocking_offset,3); +#endif + output_try_write_some(obj); + o->mode=O_RUN; /* Hubbe */ + } + } + next=o->next; + free_object(obj); + } + + free_object(THISOBJ); +} + +/* Let's guess what this function does.... + * + */ +static INLINE void input_finish(void) +{ + struct input *i; + + while(1) + { + i=THIS->firstinput->next; + free_input(THIS->firstinput); + THIS->firstinput=i; + + if(!i) break; + + switch(i->type) + { + case I_OBJ: + THIS->sleeping=0; + push_callback(offset_input_read_callback); + push_int(0); + push_callback(offset_input_close_callback); + apply_low(i->u.obj,i->set_nonblocking_offset,3); + pop_stack(); + return; + + case I_MMAP: + if (THIS->fd==-1) return; + continue; + + case I_STRING: + append_buffer(i->u.str); + } + } + THIS->sleeping=0; + + low_start(); + finished_p(); +} + +/* This function reads some data from the file cache.. + * Called when we want some data to send. + */ +static INLINE struct pike_string* gimme_some_data(unsigned long pos) +{ + struct buffer *b; + unsigned long len; + + /* We have a file cache, read from it */ + if (THIS->fd!=-1) + { + if (THIS->pos<=pos) return NULL; /* no data */ + len=THIS->pos-pos; + if (len>READ_BUFFER_SIZE) len=READ_BUFFER_SIZE; + lseek(THIS->fd,pos,0); /* SEEK_SET */ + read(THIS->fd,static_buffer,len); + return make_shared_binary_string(static_buffer,len); + } + + if (pos<THIS->pos) + return make_shared_string("buffer underflow"); /* shit */ + + /* We want something in the next buffer */ + while (THIS->firstbuffer && pos>=THIS->pos+THIS->firstbuffer->s->len) + { + /* Free the first buffer, and update THIS->pos */ + b=THIS->firstbuffer; + THIS->pos+=b->s->len; + THIS->bytes_in_buffer-=b->s->len; + THIS->firstbuffer=b->next; + if (!b->next) + THIS->lastbuffer=NULL; + sbuffers-=b->s->len; + nbuffers--; + free_string(b->s); + free((char *)b); + + /* Wake up first input if it was sleeping and we + * have room for more in the buffer. + */ + if (THIS->sleeping && + THIS->firstinput && + THIS->bytes_in_buffer<MAX_BYTES_IN_BUFFER) + { + THIS->sleeping=0; + push_callback(offset_input_read_callback); + push_int(0); + push_callback(offset_input_close_callback); + apply(THIS->firstinput->u.obj, "set_nonblocking", 3); + pop_stack(); + } + } + + while (!THIS->firstbuffer) + { + if (THIS->firstinput) + { +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + if (THIS->firstinput->type==I_MMAP) + { + if (pos >= THIS->firstinput->len + THIS->pos) /* end of mmap */ + { + THIS->pos+=THIS->firstinput->len; + input_finish(); + continue; + } + len=THIS->firstinput->len+THIS->pos-pos; + if (len>READ_BUFFER_SIZE) len=READ_BUFFER_SIZE; + return make_shared_binary_string(THIS->firstinput->u.mmap+ + pos-THIS->pos, + len); + } + else +#endif + if (THIS->firstinput->type!=I_OBJ) + { + input_finish(); /* shouldn't be anything else ... maybe a finished object */ + } + } + return NULL; /* no data */ + } + + if (pos==THIS->pos) + { + THIS->firstbuffer->s->refs++; + return THIS->firstbuffer->s; + } + return make_shared_binary_string(THIS->firstbuffer->s->str+ + pos-THIS->pos, + THIS->firstbuffer->s->len- + pos+THIS->pos); +} + + +/* + * close and free the contents of a struct output + * Note that the output struct is not freed or unlinked here, + * that is taken care of later. + */ +static INLINE void output_finish(struct object *obj) +{ + struct output *o; + + o=(struct output *)(obj->storage); + + if (o->obj) + { + if(o->obj->prog) + { +#ifdef BLOCKING_CLOSE + apply_low(o->obj,o->set_blocking_offset,0); + pop_stack(); +#endif + push_int(0); + apply(o->obj,"set_id",1); + pop_stack(); + + apply(o->obj,"close",0); + pop_stack(); + if(!THISOBJ->prog) + error("Pipe done callback destructed pipe.\n"); + destruct(o->obj); + } + free_object(o->obj); + noutputs--; + o->obj=NULL; + + THIS->living_outputs--; + + finished_p(); /* Moved by per, one line down.. :) */ + + free_object(THISOBJ); /* What? /Hubbe */ + } +} + +/* + * Try to write some data to our precious output + */ +static INLINE void output_try_write_some(struct object *obj) +{ + struct output *out; + struct pike_string *s; + unsigned long len; + INT32 ret; + + out=(struct output*)(obj->storage); + +#ifdef INSISTANT_WRITE + do + { +#endif + /* Get some data to write */ + s=gimme_some_data(out->pos); + if (!s) /* out of data */ + { + /* out of data, goto sleep */ + if (!THIS->firstinput || !out->obj->prog) /* end of life */ + { + output_finish(obj); + } + else + { +#if 0 + apply_low(out->obj, out->set_blocking_offset, 0); + pop_stack(); /* from apply */ +#endif + out->mode=O_SLEEP; + } + return; + } + len=s->len; + push_string(s); + apply_low(out->obj,out->write_offset,1); + out->mode=O_RUN; + + ret=-1; + if(sp[-1].type == T_INT) ret=sp[-1].u.integer; + pop_stack(); + + if (ret==-1) /* error, byebye */ + { + output_finish(obj); + return; + } + out->pos+=ret; + +#ifdef INSISTANT_WRITE + } while(ret == len); +#endif +} + +/********** methods *********************************************************/ + +/* Add an input to this pipe */ +static void pipe_input(INT32 args) +{ + struct input *i; + int fd=-1; /* Per, one less warning to worry about... */ + char *m; + struct stat s; + struct object *obj; + + if (args<1 || sp[-args].type != T_OBJECT) + error("Bad/missing argument 1 to pipe->input().\n"); + + obj=sp[-args].u.object; + if(!obj || !obj->prog) + error("pipe->input() on destructed object.\n"); + + push_int(0); + apply(sp[-args-1].u.object,"set_id", 1); + pop_stack(); + + i=new_input(); + +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) + + /* We do not handle mmaps if we have a buffer */ + if(THIS->fd == -1) + { + apply(obj, "query_fd", 0); + if(sp[-1].type == T_INT) fd=sp[-1].u.integer; + pop_stack(); + + if (fd != -1 && fstat(fd,&s)==0) + { + if(S_ISREG(s.st_mode) /* regular file */ + && ((long)(m=(char *)mmap(0,s.st_size,PROT_READ, + MAP_FILE|MAP_SHARED,fd,0))!=-1)) + { + mmapped += s.st_size; + + i->type=I_MMAP; + i->len=s.st_size; + i->u.mmap=m; + + pop_n_elems(args); + push_int(0); + return; + } + } + } +#endif + + i->u.obj=obj; + nobjects++; + i->type=I_OBJ; + i->u.obj->refs++; + i->set_nonblocking_offset=find_identifier("set_nonblocking",i->u.obj->prog); + i->set_blocking_offset=find_identifier("set_blocking",i->u.obj->prog); + + if (i->set_nonblocking_offset<0 || + i->set_blocking_offset<0) + { + free_object(i->u.obj); + i->u.obj=NULL; + i->type=I_NONE; + + nobjects--; + error("illegal file object%s%s\n", + ((i->set_nonblocking_offset<0)?"; no set_nonblocking":""), + ((i->set_blocking_offset<0)?"; no set_blocking":"")); + } + + if (i==THIS->firstinput) + { + push_callback(offset_input_read_callback); + push_int(0); + push_callback(offset_input_close_callback); + apply_low(i->u.obj,i->set_nonblocking_offset,3); + pop_stack(); + } + else + { + /* DOESN'T WORK!!! */ + push_int(0); + push_int(0); + push_callback(offset_input_close_callback); + apply_low(i->u.obj,i->set_nonblocking_offset,3); + pop_stack(); + } + + pop_n_elems(args); + push_int(0); +} + +static void pipe_write(INT32 args) +{ + struct input *i; + + if (args<1 || sp[-args].type!=T_STRING) + error("illegal argument to pipe->write()\n"); + + if (!THIS->firstinput) + { + append_buffer(sp[-args].u.string); + pop_n_elems(args); + push_int(0); + return; + } + + i=new_input(); + i->type=I_STRING; + nstrings++; + i->u.str=sp[-args].u.string; + i->u.str->refs++; + pop_n_elems(args-1); +} + +void f_mark_fd(INT32 args); + +static void pipe_output(INT32 args) +{ + struct object *obj; + struct output *o; + int fd; + struct stat s; + struct buffer *b; + + if (args<1 || + sp[-args].type != T_OBJECT || + !sp[-args].u.object || + !sp[-args].u.object->prog) + error("Bad/missing argument 1 to pipe->output().\n"); + + if (THIS->fd==-1) /* no buffer */ + { + /* test if usable as buffer */ + apply(sp[-args].u.object,"query_fd",0); + + if ((sp[-1].type==T_INT) + && (fd=sp[-1].u.integer)>=0 + && (fstat(fd,&s)==0) + && S_ISREG(s.st_mode) + && (THIS->fd=dup(fd))!=-1 ) + { + push_int(THIS->fd); + push_string(make_shared_string("pipe.c: file buffer")); + f_mark_fd(2); + pop_stack(); + + THIS->living_outputs++; + + while (THIS->firstbuffer) + { + b=THIS->firstbuffer; + THIS->firstbuffer=b->next; + lseek(THIS->fd,THIS->pos,0); + write(THIS->fd,b->s->str,b->s->len); + sbuffers-=b->s->len; + nbuffers--; + free_string(b->s); + free((char *)b); + } + THIS->lastbuffer=NULL; + + THIS->pos=0; + push_int(0); + apply(sp[-args-2].u.object,"set_id", 1); + pop_n_elems(args+2); /* ... and from apply x 2 */ + return; + } + pop_stack(); /* from apply */ + } + + THIS->living_outputs++; + THISOBJ->refs++; /* Weird */ + + /* Allocate a new struct output */ + obj=clone(output_program,0); + o=(struct output *)(obj->storage); + o->next=THIS->firstoutput; + THIS->firstoutput=obj; + noutputs++; + o->obj=NULL; + + o->obj=sp[-args].u.object; + o->obj->refs++; + + o->write_offset=find_identifier("write",o->obj->prog); + o->set_nonblocking_offset=find_identifier("set_nonblocking",o->obj->prog); + o->set_blocking_offset=find_identifier("set_blocking",o->obj->prog); + + if (o->write_offset<0 || o->set_nonblocking_offset<0 || + o->set_blocking_offset<0) + { + free_object(o->obj); + error("illegal file object%s%s%s\n", + ((o->write_offset<0)?"; no write":""), + ((o->set_nonblocking_offset<0)?"; no set_nonblocking":""), + ((o->set_blocking_offset<0)?"; no set_blocking":"")); + } + + o->mode=O_RUN; + o->pos=0; + + push_object(obj); + obj->refs++; + apply(o->obj,"set_id",1); + pop_stack(); + + push_int(0); + push_callback(offset_output_write_callback); + push_callback(offset_output_close_callback); + apply_low(o->obj,o->set_nonblocking_offset,3); + pop_stack(); + + pop_n_elems(args-1); +} + +static void pipe_set_done_callback(INT32 args) +{ + if (args==0) + { + free_svalue(&THIS->done_callback); + THIS->done_callback.type=T_INT; + return; + } + if (args<1 || (sp[-args].type!=T_FUNCTION && sp[-args].type!=T_ARRAY)) + error("Illegal argument to set_done_callback()\n"); + + if (args>1) + { + free_svalue(&THIS->id); + assign_svalue_no_free(&(THIS->id),sp-args+1); + } + + free_svalue(&THIS->done_callback); + assign_svalue_no_free(&(THIS->done_callback),sp-args); + pop_n_elems(args-1); +} + +static void pipe_set_output_closed_callback(INT32 args) +{ + if (args==0) + { + free_svalue(&THIS->done_callback); + THIS->output_closed_callback.type=T_INT; + return; + } + if (args<1 || (sp[-args].type!=T_FUNCTION && sp[-args].type!=T_ARRAY)) + error("Illegal argument to set_output_closed_callback()\n"); + + if (args>1) + { + free_svalue(&THIS->id); + assign_svalue_no_free(&(THIS->id),sp-args+1); + } + free_svalue(&THIS->output_closed_callback); + assign_svalue_no_free(&(THIS->output_closed_callback),sp-args); + pop_n_elems(args-1); +} + +static void pipe_finish(INT32 args) +{ + pop_n_elems(args); + push_int(0); + pipe_done(); +} + +static void pipe_start(INT32 args) /* force start */ +{ + low_start(); + if(args) + pop_n_elems(args-1); +} + +/********** callbacks *******************************************************/ + +static void pipe_write_output_callback(INT32 args) +{ + if (args<1 || sp[-args].type!=T_OBJECT) + error("Illegal argument to pipe->write_output_callback\n"); + + if(!sp[-args].u.object->prog) return; + + if(sp[-args].u.object->prog != output_program) + error("Illegal argument to pipe->write_output_callback\n"); + + output_try_write_some(sp[-args].u.object); + pop_n_elems(args-1); +} + +static void pipe_close_output_callback(INT32 args) +{ + struct output *o; + if (args<1 || sp[-args].type!=T_OBJECT) + + if(!sp[-args].u.object->prog) return; + + if(sp[-args].u.object->prog != output_program) + error("Illegal argument to pipe->close_output_callback\n"); + + o=(struct output *)(sp[-args].u.object->storage); + + if (THIS->output_closed_callback.type!=T_INT) + { + assign_svalue_no_free(sp++,&THIS->id); + push_object(o->obj); + apply_svalue(&(THIS->output_closed_callback),2); + pop_stack(); + } + + output_finish(sp[-args].u.object); + pop_n_elems(args-1); +} + +static void pipe_read_input_callback(INT32 args) +{ + struct input *i; + struct pike_string *s; + + if (args<2 || sp[1-args].type!=T_STRING) + error("Illegal argument to pipe->read_input_callback\n"); + + i=THIS->firstinput; + + if (!i) + error("Pipe read callback without any inputs left.\n"); + + s=sp[1-args].u.string; + + if(append_buffer(s)) + { + /* THIS DOES NOT WORK */ + push_int(0); + push_int(0); + push_callback(offset_input_close_callback); + apply_low(i->u.obj,i->set_nonblocking_offset,3); + pop_stack(); + THIS->sleeping=1; + } + + low_start(); + pop_n_elems(args-1); +} + +static void pipe_close_input_callback(INT32 args) +{ + struct input *i; + i=THIS->firstinput; + + if(!i) + error("Input close callback without inputs!\n"); + + if(i->type != I_OBJ) + error("Premature close callback on pipe!.\n"); + + if (i->u.obj->prog) + { +#ifdef BLOCKING_CLOSE + apply_low(i->u.obj,i->set_blocking_offset,0); + pop_stack(); +#endif + apply(i->u.obj,"close",0); + pop_stack(); + } + nobjects--; + free_object(i->u.obj); + i->type=I_NONE; + + input_finish(); + if(args) + pop_n_elems(args-1); +} + +static void pipe_version(INT32 args) +{ + pop_n_elems(args); + push_string(make_shared_string("PIPE ver 2.0")); +} + +/********** init/exit *******************************************************/ + +void close_and_free_everything(struct object *thisobj,struct pipe *p) +{ + struct buffer *b; + struct input *i; + struct output *o; + struct object *obj; + + p->done=1; + + if (thisobj) + thisobj->refs++; /* don't kill object during this */ + + while (p->firstbuffer) + { + b=p->firstbuffer; + p->firstbuffer=b->next; + sbuffers-=b->s->len; + nbuffers--; + free_string(b->s); + b->next=NULL; + free((char *)b); /* Hubbe */ + } + p->lastbuffer=NULL; + + + while (p->firstinput) + { + i=p->firstinput; + p->firstinput=i->next; + free_input(i); + } + p->lastinput=NULL; + + while (p->firstoutput) + { + obj=p->firstoutput; + o=(struct output *)(obj->storage); + p->firstoutput=o->next; + output_finish(obj); + free_object(obj); + } + if (p->fd!=-1) + { + close(p->fd); + p->fd=-1; + } + + p->living_outputs=0; + + if (thisobj) + free_object(thisobj); + + free_svalue(& p->done_callback); + free_svalue(& p->output_closed_callback); + free_svalue(& p->id); + + p->done_callback.type=T_INT; + p->output_closed_callback.type=T_INT; + p->id.type=T_INT; + + p->done=0; +} + +static void init_pipe_struct(struct object *o) +{ + THIS->firstbuffer=THIS->lastbuffer=NULL; + THIS->firstinput=THIS->lastinput=NULL; + THIS->firstoutput=NULL; + THIS->bytes_in_buffer=0; + THIS->pos=0; + THIS->sleeping=0; + THIS->done=0; + THIS->fd=-1; + THIS->done_callback.type=T_INT; + THIS->output_closed_callback.type=T_INT; + THIS->id.type=T_INT; + THIS->id.u.integer=0; + THIS->living_outputs=0; +} + +static void exit_pipe_struct(struct object *o) +{ + close_and_free_everything(NULL,THIS); +} + +static void exit_output_struct(struct object *obj) +{ + struct output *o; + + o=(struct output *)(fp->current_storage); + if (o->obj) + { + if(o->obj->prog) + { +#ifdef BLOCKING_CLOSE + apply_low(o->obj,o->set_blocking_offset,0); + pop_stack(); +#endif + push_int(0); + apply(o->obj,"set_id",1); + pop_stack(); + + apply(o->obj,"close",0); + pop_stack(); + + if(!THISOBJ->prog) + error("Pipe done callback destructed pipe.\n"); + } + free_object(o->obj); + noutputs--; + o->obj=0; + } +} + +static void init_output_struct(struct object *ob) +{ + struct output *o; + o=(struct output *)(fp->current_storage); + o->obj=0; +} + + +/********** Pike init *******************************************************/ + +void port_setup_program(void); + +void f__pipe_debug(INT32 args) +{ + pop_n_elems(args); + push_int(noutputs); + push_int(ninputs); + push_int(nstrings); + push_int(nobjects); + push_int(mmapped); + push_int(nbuffers); + push_int(sbuffers); + f_aggregate(7); +} + +void pike_module_init() +{ + start_new_program(); + add_storage(sizeof(struct pipe)); + add_efun("_pipe_debug", f__pipe_debug, "function(:array)", 0); + add_function("input",pipe_input,"function(object:void)",0); + add_function("output",pipe_output,"function(object:void)",0); + add_function("write",pipe_write,"function(string:void)",0); + + add_function("start",pipe_start,"function(:void)",0); + add_function("finish",pipe_finish,"function(:void)",0); + + add_function("set_output_closed_callback",pipe_set_output_closed_callback, + "function(void|function(mixed,object:mixed),void|mixed:void)",0); + add_function("set_done_callback",pipe_set_done_callback, + "function(void|function(mixed:mixed),void|mixed:void)",0); + + add_function("_output_close_callback",pipe_close_output_callback, + "function(int:void)",0); + add_function("_input_close_callback",pipe_close_input_callback, + "function(int:void)",0); + add_function("_output_write_callback",pipe_write_output_callback, + "function(int:void)",0); + add_function("_input_read_callback",pipe_read_input_callback, + "function(int,string:void)",0); + + add_function("version",pipe_version,"function(:string)",0); + + set_init_callback(init_pipe_struct); + set_exit_callback(exit_pipe_struct); + + pipe_program=end_program(); + add_program_constant("pipe",pipe_program, 0); + + offset_output_close_callback=find_identifier("_output_close_callback", + pipe_program); + offset_input_close_callback=find_identifier("_input_close_callback", + pipe_program); + offset_output_write_callback=find_identifier("_output_write_callback", + pipe_program); + offset_input_read_callback=find_identifier("_input_read_callback", + pipe_program); + + + start_new_program(); + add_storage(sizeof(struct output)); + set_init_callback(init_output_struct); + set_exit_callback(exit_output_struct); + output_program=end_program(); + add_program_constant("__output",output_program, 0); +} + +void pike_module_exit(void) +{ + if(pipe_program) free_program(pipe_program); + pipe_program=0; + if(output_program) free_program(output_program); + output_program=0; +} + + + + + + + diff --git a/src/modules/Pipe/testsuite.in b/src/modules/Pipe/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/modules/Regexp/.cvsignore b/src/modules/Regexp/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..ed13e79dafb075dba9bee95ed9608b991ad17397 --- /dev/null +++ b/src/modules/Regexp/.cvsignore @@ -0,0 +1,7 @@ +.pure +Makefile +config.log +config.status +configure +dependencies +stamp-h diff --git a/src/modules/Regexp/.gitignore b/src/modules/Regexp/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2d448ae74bf4fb36f7a1db409d228c656ce05d72 --- /dev/null +++ b/src/modules/Regexp/.gitignore @@ -0,0 +1,7 @@ +/.pure +/Makefile +/config.log +/config.status +/configure +/dependencies +/stamp-h diff --git a/src/modules/Regexp/Makefile.in b/src/modules/Regexp/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..21853b9fc4c4f4ec9110cbd08caed4824af78aa6 --- /dev/null +++ b/src/modules/Regexp/Makefile.in @@ -0,0 +1,6 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=regexp.o glue.o + +@dynamic_module_makefile@ +@dependencies@ \ No newline at end of file diff --git a/src/modules/Regexp/configure.in b/src/modules/Regexp/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..ed3283c8eec415697210f9591f054dac166c5e4c --- /dev/null +++ b/src/modules/Regexp/configure.in @@ -0,0 +1,8 @@ +AC_INIT(regexp.c) + +sinclude(../module_configure.in) + + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) + + diff --git a/src/modules/Regexp/doc/regexp b/src/modules/Regexp/doc/regexp new file mode 100644 index 0000000000000000000000000000000000000000..b7358e616d66fdccc1ae4354f73a8d14715dafa1 --- /dev/null +++ b/src/modules/Regexp/doc/regexp @@ -0,0 +1,91 @@ +NAME + /precompiled/regexp - regexp handling module + +DESCRIPTION + /precompiled/regexp is a precompiled Pike program that interfaces a + regexp package written in C. It contains a few simple functions to + handle regexps. A short description of regexp follows: + + . Matches any character + [abc] Matches a, b or c + [a-z] Matches any character a to z inclusive + [^ac] Matches any character except a and c + (x) Matches x (x might be any regexp) If used with split, this + also puts the string matching x into the result array. + x* Matches zero or more occurances of 'x' (x may be any regexp) + x+ Matches one or more occurances of 'x' (x may be any regexp) + x|y Matches x or y. (x or y may be any regexp) + xy Matches xy (x and y may be any regexp) + ^ Matches beginning of string (but no characters) + $ Matches end of string (but no characters) + \< matches the beginning of a word (but no characters) + \> matches the end of a word (but no characters) + + Note that \ can be used to quote these characters in which case + they match themselves, nothing else. Also note that when quoting + these something in Pike you need two \ because Pike also uses + this character for quoting. + + For more information about regexps, refer to your unix manuals such + as sed or ed. + + Descriptions of all functions in /precompiled/regexp follows: + +============================================================================ +NAME + create - compile regexp + +SYNTAX + void create(); + or + void create(string regexp); + or + object clone((program)"/precompiled/file"); + or + object clone((program)"/precompiled/file",string regexp); + +DESCRIPTION + When create is called, the current regexp bound to this object is + cleared. If a string is sent to create(), this string will be compiled + to an internal representation of the regexp and bound to this object + for laters calls to match or split. Calling create() without an + argument can be used to free up a little memory after the regexp has + been used. + +SEE ALSO + builtin/clone, regexp->match + +============================================================================ +NAME + match - match a regexp + +SYNTAX + int regexp->match(string s) + +DESCRIPTION + Return 1 if s matches the regexp bound to the object regexp, + zero otherwise + +SEE ALSO + regexp->create, regexp->split + +============================================================================ +NAME + split - split a string according to a pattern + +SYNTAX + string *regexp->split(string s) + +DESCRIPTION + Works as regexp->match, but returns an array of the strings that + matched the subregexps. Subregexps are those contained in ( ) in + the regexp. Subregexps that were not matched will contain zero. + If the total regexp didn't match, zero is returned. + +BUGS + You can only have 40 subregexps. + +SEE ALSO + regexp->create, regexp->split + +============================================================================ diff --git a/src/modules/Regexp/glue.c b/src/modules/Regexp/glue.c new file mode 100644 index 0000000000000000000000000000000000000000..9da6245d8aeb83cb7574379c8f0c5a69bab9b722 --- /dev/null +++ b/src/modules/Regexp/glue.c @@ -0,0 +1,120 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#include "global.h" +#include "types.h" +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "object.h" +#include "macros.h" + +#include <regexp.h> + +struct regexp_glue +{ + struct regexp *regexp; +}; + +#define THIS ((struct regexp_glue *)(fp->current_storage)) + +static void do_free() +{ + if(THIS->regexp) + { + free((char *)THIS->regexp); + THIS->regexp=0; + } +} + +static void regexp_create(INT32 args) +{ + do_free(); + if(args) + { + if(sp[-args].type != T_STRING) + error("Bad argument 1 to regexp->create()\n"); + + THIS->regexp=regcomp(sp[-args].u.string->str, 0); + } +} + +static void regexp_match(INT32 args) +{ + int i; + if(!args) + error("Too few arguments to regexp->match()\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to regexp->match()\n"); + + i=regexec(THIS->regexp, sp[-args].u.string->str); + pop_n_elems(args); + push_int(i); +} + +static void regexp_split(INT32 args) +{ + struct pike_string *s; + struct regexp *r; + if(!args) + error("Too few arguments to regexp->split()\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to regexp->split()\n"); + + s=sp[-args].u.string; + if(regexec(r=THIS->regexp, s->str)) + { + int i,j; + s->refs++; + pop_n_elems(args); + for(j=i=1;i<NSUBEXP;i++) + { + if(!r->startp[i] || !r->endp[i]) + { + push_int(0); + }else{ + push_string(make_shared_binary_string(r->startp[i], + r->endp[i]-r->startp[i])); + j=i; + } + } + if(j<i-1) pop_n_elems(i-j-1); + push_array(aggregate_array(j)); + free_string(s); + }else{ + pop_n_elems(args); + push_int(0); + } +} + +static void init_regexp_glue(struct object *o) +{ + THIS->regexp=0; +} + +static void exit_regexp_glue(struct object *o) +{ + do_free(); +} + + +void pike_module_exit(void) {} + +void pike_module_init(void) +{ + start_new_program(); + add_storage(sizeof(struct regexp_glue)); + + add_function("create",regexp_create,"function(void|string:void)",0); + add_function("match",regexp_match,"function(string:int)",0); + add_function("split",regexp_split,"function(string:string*)",0); + + set_init_callback(init_regexp_glue); + set_exit_callback(exit_regexp_glue); + end_class("_module_value", 0); +} diff --git a/src/modules/Regexp/pike_regexp.c b/src/modules/Regexp/pike_regexp.c new file mode 100644 index 0000000000000000000000000000000000000000..08c988177cecbba9f3c2216dc13fc8095d3fa185 --- /dev/null +++ b/src/modules/Regexp/pike_regexp.c @@ -0,0 +1,1408 @@ +/* + * + * regexp.c - regular expression matching + * + * DESCRIPTION + * + * Underneath the reformatting and comment blocks which were added to + * make it consistent with the rest of the code, you will find a + * modified version of Henry Specer's regular expression library. + * Henry's functions were modified to provide the minimal regular + * expression matching, as required by P1003. Henry's code was + * copyrighted, and copy of the copyright message and restrictions + * are provided, verbatim, below: + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * + * This version modified by Ian Phillipps to return pointer to terminating + * NUL on substitution string. [ Temp mail address ex-igp@camcon.co.uk ] + * + * Altered by amylaar to support excompatible option and the + * operators \< and >\ . ( 7.Sep. 1991 ) + * + * regsub altered by amylaar to take an additional parameter specifying + * maximum number of bytes that can be written to the memory region + * pointed to by dest + * + * Also altered by Fredrik Hubinette to handle the + operator and + * eight-bit chars. Mars 22 1996 + * + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + * + * AUTHORS + * + * Mark H. Colburn, NAPS International (mark@jhereg.mn.org) + * Henry Spencer, University of Torronto (henry@utzoo.edu) + * + * Sponsored by The USENIX Association for public distribution. + * + */ + +/* Headers */ +#include "global.h" +#include <ctype.h> +#include "regexp.h" +#include "memory.h" +#include "error.h" + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "nxt" pointer, possibly plus an operand. "Nxt" pointers of + * all nodes except BRANCH implement concatenation; a "nxt" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this + * string. */ +#define BRANCH 6 /* node Match this alternative, or the + * nxt... */ +#define BACK 7 /* no Match "", "nxt" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more + * times. */ +#define WORDSTART 11 /* node matching a start of a word */ +#define WORDEND 12 /* node matching an end of a word */ +#define OPEN 20 /* no Mark this point in input as start of + * #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "nxt" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "nxt" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "nxt" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "nxt" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR complex '*', are implemented as circular BRANCH structures + * using BACK. Simple cases (one character per match) are + * implemented with STAR for speed and to minimize recursive + * plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "nxt" pointer. + * "Nxt" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "nxt" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 + +/* + * Utility definitions. + */ + +#define regerror(X) error("Regexp: %s\n",X); +#define SPECIAL 0x100 +#define LBRAC ('('|SPECIAL) +#define RBRAC (')'|SPECIAL) +#define ASTERIX ('*'|SPECIAL) +#define PLUS ('+'|SPECIAL) +#define OR_OP ('|'|SPECIAL) +#define DOLLAR ('$'|SPECIAL) +#define DOT ('.'|SPECIAL) +#define CARET ('^'|SPECIAL) +#define LSQBRAC ('['|SPECIAL) +#define RSQBRAC (']'|SPECIAL) +#define LSHBRAC ('<'|SPECIAL) +#define RSHBRAC ('>'|SPECIAL) +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == ASTERIX || (c)==PLUS) +#define META "^$.[()|*+\\" +#ifndef CHARBITS +#define CHARBITS 0xff +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif +#define ISWORDPART(c) ( isalnum(c) || (c) == '_' ) + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR operand. */ +#define SPSTART 04 /* Starts with * */ +#define WORST 0 /* Worst case. */ + +/* + * Global work variables for regcomp(). + */ +static short *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +#ifndef STATIC +#define STATIC static +#endif +STATIC char *reg(); +STATIC char *regbranch(); +STATIC char *regpiece(); +STATIC char *regatom(); +STATIC char *regnode(); +STATIC char *regnext(); +STATIC void regc(); +STATIC void reginsert(); +STATIC void regtail(); +STATIC void regoptail(); + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp *regcomp(exp,excompat) +char *exp; +int excompat; /* \( \) operators like in unix ex */ +{ + register regexp *r; + register char *scan; + register char *longest; + register int len; + int flags; + short *exp2,*dest,c; + + if (exp == (char *)NULL) + FAIL("NULL argument"); + + exp2=(short*)xalloc( (strlen(exp)+1) * (sizeof(short[8])/sizeof(char[8])) ); + for ( scan=exp,dest=exp2;( c= UCHARAT(scan++)); ) { + switch (c) { + case '(': + case ')': + *dest++ = excompat ? c : c | SPECIAL; + break; + case '.': + case '*': + case '+': + case '|': + case '$': + case '^': + case '[': + case ']': + *dest++ = c | SPECIAL; + break; + case '\\': + switch ( c = *scan++ ) { + case '(': + case ')': + *dest++ = excompat ? c | SPECIAL : c; + break; + case '<': + case '>': + *dest++ = c | SPECIAL; + break; + case '{': + case '}': + FAIL("sorry, unimplemented operator"); + case 'b': *dest++ = '\b'; break; + case 't': *dest++ = '\t'; break; + case 'r': *dest++ = '\r'; break; + default: + *dest++ = c; + } + break; + default: + *dest++ = c; + } + } + *dest=0; + /* First pass: determine size, legality. */ + regparse = exp2; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == (char *)NULL) + return ((regexp *)NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *) xalloc(sizeof(regexp) + (unsigned) regsize); + if (r == (regexp *) NULL) + FAIL("out of space"); + + /* Second pass: emit code. */ + regparse = exp2; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return ((regexp *) NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program + 1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the longest + * literal string that must appear and make it the regmust. Resolve + * ties in favor of later strings, since the regstart check works + * with the beginning of the r.e. and avoiding duplication + * strengthens checking. Not a strong reason, but sufficient in the + * absence of others. + */ + if (flags & SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && + (int)strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + free((char*)exp2); + return (r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char *reg(paren, flagp) +int paren; /* Parenthesized? */ +int *flagp; +{ + register char *ret; + register char *br; + register char *ender; + register int parno=0; /* make gcc happy */ + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN + parno); + } else + ret = (char *)NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == (char *)NULL) + return ((char *)NULL); + if (ret != (char *)NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + while (*regparse == OR_OP) { + regparse++; + br = regbranch(&flags); + if (br == (char *)NULL) + return ((char *)NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags & HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE + parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != (char *)NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != RBRAC) { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == RBRAC) { + FAIL("unmatched ()"); + } else + FAIL("junk on end");/* "Can't happen". */ + /* NOTREACHED */ + } + return (ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char *regbranch(flagp) +int *flagp; +{ + register char *ret; + register char *chain; + register char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = (char *)NULL; + while (*regparse != '\0' && *regparse != OR_OP && *regparse != RBRAC) { + latest = regpiece(&flags); + if (latest == (char *)NULL) + return ((char *)NULL); + *flagp |= flags & HASWIDTH; + if (chain == (char *)NULL) /* First piece. */ + *flagp |= flags & SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == (char *)NULL) /* Loop ran zero times. */ + regnode(NOTHING); + + return (ret); +} + +/* + - regpiece - something followed by possible [*] + * + * Note that the branching code sequence used for * is somewhat optimized: + * they use the same NOTHING node as both the endmarker for their branch + * list and the body of the last branch. It might seem that this node could + * be dispensed with entirely, but the endmarker role is not redundant. + */ +static char *regpiece(flagp) +int *flagp; +{ + register char *ret; + register short op; + /* register char *nxt; */ + int flags; + + ret = regatom(&flags); + if (ret == (char *)NULL) + return ((char *)NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return (ret); + } + if (!(flags & HASWIDTH)) + FAIL("* or + operand could be empty"); + *flagp = (WORST | SPSTART); + + if(op == ASTERIX) + { + if (flags & SIMPLE) + { + reginsert(STAR, ret); + } + else + { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } + } + else if(op == PLUS) + { + /* Emit a+ as (a&) where & means "self" /Fredrik Hubinette */ + char *tmp; + tmp=regnode(BACK); + reginsert(BRANCH, tmp); + regtail(ret, tmp); + regoptail(tmp, ret); + regtail(ret, regnode(BRANCH)); + regtail(ret, regnode(NOTHING)); + } + + regparse++; + if (ISMULT(*regparse)) + FAIL("nested * or +"); + + return (ret); +} + + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. + */ +static char *regatom(flagp) +int *flagp; +{ + register char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + case CARET: + ret = regnode(BOL); + break; + case DOLLAR: + ret = regnode(EOL); + break; + case DOT: + ret = regnode(ANY); + *flagp |= HASWIDTH | SIMPLE; + break; + case LSHBRAC: + ret = regnode(WORDSTART); + break; + case RSHBRAC: + ret = regnode(WORDEND); + break; + case LSQBRAC:{ + register int class; + register int classend; + + if (*regparse == CARET) { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == RSQBRAC || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != RSQBRAC) { + if (*regparse == '-') { + regparse++; + if (*regparse == RSQBRAC || *regparse == '\0') + regc('-'); + else { + class = (CHARBITS & *(regparse - 2)) + 1; + classend = (CHARBITS & *(regparse)); + if (class > classend + 1) + FAIL("invalid [] range"); + for (; class <= classend; class++) + regc(class); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != RSQBRAC) + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH | SIMPLE; + } + break; + case LBRAC: + ret = reg(1, &flags); + if (ret == (char *)NULL) + return ((char *)NULL); + *flagp |= flags & (HASWIDTH | SPSTART); + break; + case '\0': + case OR_OP: + case RBRAC: + FAIL("internal urp"); /* Supposed to be caught earlier. */ + + case ASTERIX: + FAIL("* follows nothing\n"); + + default:{ + register int len; + register short ender; + + regparse--; + for (len=0; regparse[len] && + !(regparse[len]&SPECIAL) && regparse[len] != RSQBRAC; len++) ; + if (len <= 0) + { + FAIL("internal disaster"); + } + ender = *(regparse + len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of * operand. */ + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } + break; + } + + return (ret); +} + +/* + - regnode - emit a node + */ +static char *regnode(op) +char op; +{ + register char *ret; + register char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return (ret); + } + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "nxt" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return (ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void regc(b) +char b; +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void reginsert(op, opnd) +char op; +char *opnd; +{ + register char *src; + register char *dst; + register char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void regtail(p, val) +char *p; +char *val; +{ + register char *scan; + register char *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == (char *)NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan + 1) = (offset >> 8) & 0377; + *(scan + 2) = offset & 0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +static void regoptail(p, val) +char *p; +char *val; +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == (char *)NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* + * Forwards. + */ +STATIC int regtry(); +STATIC int regmatch(); +STATIC int regrepeat(); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int regexec(prog, string) +register regexp *prog; +register char *string; +{ + register char *s; + + /* Be paranoid... */ + if (prog == (regexp *)NULL || string == (char *)NULL) { + regerror("NULL parameter"); + return (0); + } + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return (0); + } + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != (char *)NULL) { + s = string; + while ((s = STRCHR(s, prog->regmust[0])) != (char *)NULL) { + if (strncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == (char *)NULL) /* Not present. */ + return (0); + } + /* Mark beginning of line for ^ . */ + regbol = string; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return (regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = STRCHR(s, prog->regstart)) != (char *)NULL) { + if (regtry(prog, s)) + return (1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return (1); + } while (*s++ != '\0'); + + /* Failure. */ + return (0); +} + +/* + - regtry - try match at specific point + */ +#ifdef __STDC__ + +static int regtry(regexp *prog, char *string) + +#else + +static int regtry(prog, string) +regexp *prog; +char *string; + +#endif +{ + register int i; + register char **sp; + register char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = (char *)NULL; + *ep++ = (char *)NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return (1); + } else + return (0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +#ifdef __STDC__ + +static int regmatch(char *prog) + +#else + +static int regmatch(prog) +char *prog; + +#endif +{ + register char *scan; /* Current node. */ + char *nxt; /* nxt node. */ + + scan = prog; +#ifdef DEBUG + if (scan != (char *)NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != (char *)NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + nxt = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return (0); + break; + case EOL: + if (*reginput != '\0') + return (0); + break; + case ANY: + if (*reginput == '\0') + return (0); + reginput++; + break; + case WORDSTART: + if (reginput == regbol) + break; + if (*reginput == '\0' || + ISWORDPART( *(reginput-1) ) || !ISWORDPART( *reginput ) ) + return (0); + break; + case WORDEND: + if (*reginput == '\0') + break; + if ( reginput == regbol || + !ISWORDPART( *(reginput-1) ) || ISWORDPART( *reginput ) ) + return (0); + break; + case EXACTLY:{ + register int len; + register char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return (0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return (0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || + STRCHR(OPERAND(scan), *reginput) == (char *)NULL) + return (0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || + STRCHR(OPERAND(scan), *reginput) != (char *)NULL) + return (0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9:{ + register int no; + register char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(nxt)) { + /* + * Don't set startp if some later invocation of the same + * parentheses already has. + */ + if (regstartp[no] == (char *)NULL) + regstartp[no] = save; + return (1); + } else + return (0); + } + + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9:{ + register int no; + register char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(nxt)) { + /* + * Don't set endp if some later invocation of the same + * parentheses already has. + */ + if (regendp[no] == (char *)NULL) + regendp[no] = save; + return (1); + } else + return (0); + } + + case BRANCH:{ + register char *save; + + if (OP(nxt) != BRANCH) /* No choice. */ + nxt = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return (1); + reginput = save; + scan = regnext(scan); + } while (scan != (char *)NULL && OP(scan) == BRANCH); + return (0); + /* NOTREACHED */ + } + } + break; + case STAR:{ + register char nextch; + register int no; + register char *save; + register int minimum; + + /* + * Lookahead to avoid useless match attempts when we know + * what character comes next. + */ + nextch = '\0'; + if (OP(nxt) == EXACTLY) + nextch = *OPERAND(nxt); + minimum = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= minimum) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(nxt)) + return (1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return (0); + } + + case END: + return (1); /* Success! */ + + default: + regerror("memory corruption"); + return (0); + + } + + scan = nxt; + } + + /* + * We get here only if there's trouble -- normally "case END" is the + * terminating point. + */ + regerror("corrupted pointers"); + return (0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +#ifdef __STDC__ + +static int regrepeat(char *p) + +#else + +static int regrepeat(p) +char *p; + +#endif +{ + register int count = 0; + register char *scan; + register char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && STRCHR(opnd, *scan) != (char *)NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && STRCHR(opnd, *scan) == (char *)NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return (count); +} + + +/* + - regnext - dig the "nxt" pointer out of a node + */ +#ifdef __STDC__ + +static char *regnext(register char *p) + +#else + +static char *regnext(p) +register char *p; + +#endif +{ + register int offset; + + if (p == ®dummy) + return ((char *)NULL); + + offset = NEXT(p); + if (offset == 0) + return ((char *)NULL); + + if (OP(p) == BACK) + return (p - offset); + else + return (p + offset); +} + +#ifdef DEBUG + +STATIC char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +#ifdef __STDC__ + +void regdump(regexp *r) + +#else + +void regdump(r) +regexp *r; + +#endif +{ + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *nxt; + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2ld%s", (long)(s - r->program), regprop(s)); /* Where, what. */ + nxt = regnext(s); + if (nxt == (char *)NULL) /* nxt ptr. */ + printf("(0)"); + else + printf("(%ld)", (long)( (s - r->program) + (nxt - s))); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') + printf("start `%c' ", r->regstart); + if (r->reganch) + printf("anchored "); + if (r->regmust != (char *)NULL) + printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +#ifdef __STDC__ + +static char *regprop(char *op) + +#else + +static char *regprop(op) +char *op; + +#endif +{ + register char *p; + static char buf[50]; + + strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9: + sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN); + p = (char *)NULL; + break; + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9: + sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE); + p = (char *)NULL; + break; + case STAR: + p = "STAR"; + break; + default: + regerror("corrupted opcode"); + p=(char *)NULL; + break; + } + if (p != (char *)NULL) + strcat(buf, p); + return (buf); +} +#endif + +/* + - regsub - perform substitutions after a regexp match + */ +#ifdef __STDC__ + +char *regsub(regexp *prog, char *source, char *dest, int n) + +#else + +char *regsub(prog, source, dest, n) +regexp *prog; +char *source; +char *dest; +int n; + +#endif +{ + register char *src; + register char *dst; + register char c; + register int no; + register int len; +#ifndef strncpy + extern char *strncpy(); +#endif + + if (prog == (regexp *)NULL || + source == (char *)NULL || dest == (char *)NULL) { + regerror("NULL parm to regsub"); + return NULL; + } + if (UCHARAT(prog->program) != MAGIC) { + regerror("damaged regexp fed to regsub"); + return NULL; + } + src = source; + dst = dest; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && '0' <= *src && *src <= '9') + no = *src++ - '0'; + else + no = -1; + + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) + c = *src++; + if (--n < 0) { /* amylaar */ + regerror("line too long"); + return NULL; + } + *dst++ = c; + } else if (prog->startp[no] != (char *)NULL && + prog->endp[no] != (char *)NULL) { + len = prog->endp[no] - prog->startp[no]; + if ( (n-=len) < 0 ) { /* amylaar */ + regerror("line too long"); + return NULL; + } + strncpy(dst, prog->startp[no], len); + dst += len; + if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */ + regerror("damaged match string"); + return NULL; + } + } + } + if (--n < 0) { /* amylaar */ + regerror("line too long"); + return NULL; + } + *dst = '\0'; + return dst; +} + + +#if 0 /* Use the local regerror() in ed.c */ +#ifdef __STDC__ + +void regerror(char *s) + +#else + +void regerror(s) +char *s; + +#endif +{ + fprintf(stderr, "regexp(3): %s", s); + exit(1); +} +#endif /* 0 */ diff --git a/src/modules/Regexp/pike_regexp.h b/src/modules/Regexp/pike_regexp.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa51439e9d3a849345c4e2039523083d5f85639 --- /dev/null +++ b/src/modules/Regexp/pike_regexp.h @@ -0,0 +1,33 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#ifndef REGEXP_H +#define REGEXP_H +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ + +#define NSUBEXP 40 +typedef struct regexp +{ + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + + + +extern regexp *regcomp(); +extern int regexec(); +extern char *regsub(); +extern void regerror(); +#endif diff --git a/src/modules/Regexp/testsuite.in b/src/modules/Regexp/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..f20433cf9c61b7a05deb69501b0071cf92bf0206 --- /dev/null +++ b/src/modules/Regexp/testsuite.in @@ -0,0 +1,34 @@ +// - Here we try the regexp module +test_true([[programp(regexp)]]) +test_any([[object o; o=clone(regexp); destruct(o); return 1]],1) + +// regexp->create +test_any([[object o; o=regexp("^.*$"); destruct(o); return 1]],1) + +// regexp->match +test_eq([[regexp("^.*$")->match("")]],1) +test_eq([[regexp(".*")->match("foo")]],1) +test_eq([[regexp("^.*$")->match("a")]],1) +test_eq([[regexp("^.*$")->match("-")]],1) +test_eq([[regexp("^$")->match("")]],1) +test_eq([[regexp("^.$")->match("a")]],1) +test_eq([[regexp("^.$")->match("-")]],1) +test_eq([[regexp("^[abc]$")->match("-")]],0) +test_eq([[regexp("^[abc]$")->match("a")]],1) +test_eq([[regexp("^[abc]$")->match("c")]],1) +test_eq([[regexp("^[^abc]$")->match("-")]],1) +test_eq([[regexp("^[^abc]$")->match("a")]],0) +test_eq([[regexp("^[^abc]$")->match("c")]],0) +test_eq([[regexp("^a*$")->match("aaaa")]],1) +test_eq([[regexp("^(a|bb)*$")->match("aabbabb")]],1) +test_eq([[regexp("^(a|bb)*$")->match("")]],1) +test_eq([[regexp("^(a|bb)+$")->match("")]],0) +test_eq([[regexp("^(a|bb)+$")->match("aaa")]],1) +test_eq([[regexp("^(a|bb)+$")->match("bbb")]],0) +test_eq([[regexp("^(a|bb)+$")->match("bbaabba")]],1) +test_eq([[regexp("^a|b$")->match("a")]],1) +test_eq([[regexp("^a|b$")->match("b")]],1) + +// regexp->split +test_equal([[regexp("^(a*)[^a]*$")->split("aaabbb")]],({"aaa"})) + diff --git a/src/modules/Ssleay/.cvsignore b/src/modules/Ssleay/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..c0bdcf80041cb0f78811b96239afcd83ff815c06 --- /dev/null +++ b/src/modules/Ssleay/.cvsignore @@ -0,0 +1,8 @@ +.pure +Makefile +config.log +config.status +configure +dependencies +linker_options +stamp-h diff --git a/src/modules/Ssleay/.gitignore b/src/modules/Ssleay/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..7ed24cbd425f9796f4d1a7b2a62674aa61cf4353 --- /dev/null +++ b/src/modules/Ssleay/.gitignore @@ -0,0 +1,8 @@ +/.pure +/Makefile +/config.log +/config.status +/configure +/dependencies +/linker_options +/stamp-h diff --git a/src/modules/Ssleay/Makefile.in b/src/modules/Ssleay/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..03ef9ccd3254f8908ade770b9461f417c89c36b6 --- /dev/null +++ b/src/modules/Ssleay/Makefile.in @@ -0,0 +1,9 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +MODULE_CLAGS=@DEFS@ @CPPFLAGS@ +OBJS=ssleay.o +MODULE_LDFLAGS=@LDFLAGS@ @LIBS@ + +@dynamic_module_makefile@ +@dependencies@ + diff --git a/src/modules/Ssleay/configure.in b/src/modules/Ssleay/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..dedd922090f9bce519a760d803e963ca3327c34a --- /dev/null +++ b/src/modules/Ssleay/configure.in @@ -0,0 +1,61 @@ +AC_INIT(ssleay.c) + +sinclude(../module_configure.in) + +AC_ARG_WITH(ssleay, [ --without-ssleay no support for the secure socket protocol],[],[with_ssleay=yes]) + +if test x$with_ssleay = xyes; then + OLD_LDFLAGS=$LDFLAGS + OLD_CPPFLAGS=$CPPFLAGS + OLD_LIBS=$LIBS + + AC_MSG_CHECKING(Checking for existance of SSLeay) + + AC_CACHE_VAL(pike_cv_ssleay_exists, + [ + if test -d /usr/local/ssl ; then + pike_cv_ssleay_exists="yes" + else + pike_cv_ssleay_exists="no" + fi + ]) + + AC_MSG_RESULT($pike_cv_ssleay_exists) + + if test x$pike_cv_ssleay_exists = xyes; then + + if test -d /usr/local/ssl/lib ; then + echo Added /usr/local/ssl/lib to the library search path. + LDFLAGS="-L/usr/local/ssl/lib ${LDFLAGS}" + # link with libc first, so we get the right definition + # of crypt() + LDFLAGS="-lc ${LDFLAGS}" + fi + + if test -d /usr/local/ssl/include ; then + echo Added /usr/local/ssl/include to the include search path. + CPPFLAGS="-I/usr/local/ssl/include ${CPPFLAGS}" + fi + + pike_cv_ssleay="yes" + + AC_CHECK_LIB(crypto, ERR_print_errors_fp, [], [ pike_cv_ssleay="no" ]) + AC_CHECK_LIB(ssl, SSL_use_PrivateKey_file, [], [ pike_cv_ssleay="no" ]) + + AC_MSG_CHECKING(Supported version of SSLeay) + + AC_MSG_RESULT($pike_cv_ssleay) + + if test x$pike_cv_ssleay = xyes; then + AC_DEFINE(HAVE_SSLEAY) + else + # Restore variables, so we don't link with unnessesary libs + + LIBS=$OLD_LIBS + CPPFLAGS=$OLD_CPPFLAGS + LDFLAGS=$OLD_LDFLAGS + fi + fi +fi + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/modules/Ssleay/ssleay.c b/src/modules/Ssleay/ssleay.c new file mode 100644 index 0000000000000000000000000000000000000000..5497eefe4df4ef32d2903b4e3f63dbec69f65259 --- /dev/null +++ b/src/modules/Ssleay/ssleay.c @@ -0,0 +1,313 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ + +#include "global.h" +RCSID("$Id: ssleay.c,v 1.1 1997/02/11 08:39:19 hubbe Exp $"); +#include "types.h" +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "object.h" +#include "macros.h" +#include "backend.h" +#include "program.h" +#include "threads.h" + +#ifdef HAVE_SYS_TYPE_H +#include <sys/types.h> +#endif + +#ifdef HAVE_SSLEAY + +#include <ssl.h> +#include <crypto.h> +#include <pem.h> +#include <err.h> +#include <x509.h> + +/* SSLeay defines _ as a macro. That's Puckat(TM). */ +#undef _ + +struct ssleay_context +{ + SSL_CTX *shared; +}; + +struct ssleay_connection +{ + SSL *con; +}; + + +#define THISOBJ (fp->current_object) +#define CON (((struct ssleay_connection *) (fp->current_storage))->con) +#define CTX (((struct ssleay_context *) (fp->current_storage))->shared) + +#endif /* HAVE_SSLEAY */ + +static struct program *ssleay_program; +static struct program *ssleay_connection_program; + +#ifdef HAVE_SSLEAY + +/* Methods for ssleay_connection objects */ + +/* Arg is an ssleay object */ +void ssleay_connection_create(INT32 args) +{ + if (args < 1) + error("ssleay_connection->create: no context given\n"); + if ((sp[-args].type != T_OBJECT) + || (sp[-args].u.object->prog != ssleay_program)) + error("ssleay_connection->create: invalid argument\n"); + if (CON) + SSL_free(CON); + CON = SSL_new( ( (struct ssleay_context *) sp[-args].u.object->storage) + -> shared); + if (!CON) + { + ERR_print_errors_fp(stderr); + error("ssleay_connection->create: Could not allocate new connection\n"); + } + SSL_clear(CON); +} + +void ssleay_connection_set_fd(INT32 args) +{ + if ((args < 1) || (sp[-args].type != T_INT)) + error("ssleay_connection->set_fd: wrong type\n"); + SSL_set_fd(CON, sp[-args].u.integer); + pop_n_elems(args); +} + +void ssleay_connection_accept(INT32 args) +{ + int res; + + pop_n_elems(args); + THREADS_ALLOW(); + res = SSL_accept(CON); + THREADS_DISALLOW(); + push_int(res); +} + +void ssleay_connection_read(INT32 args) +{ + struct pike_string *s; + INT32 len; + INT32 count; + + if ((args < 1) || (sp[-args].type != T_INT)) + error("ssleay_connection->read: wrong type\n"); + len = sp[-args].u.integer; + + if (len < 0) + error("ssleay_connection->read: invalid argument\n"); + pop_n_elems(args); + + s = begin_shared_string(len); + if (len) + { + THREADS_ALLOW(); + count = SSL_read(CON, s->str, len); + THREADS_DISALLOW(); + if (count < 0) + { + free_string(end_shared_string(s)); + push_int(count); + } + else + { + s->len = count; + push_string(end_shared_string(s)); + } + } +} + +void ssleay_connection_write(INT32 args) +{ + INT32 res; + + if ((args < 1) || (sp[-args].type != T_STRING)) + error("ssleay_connection->write: wrong argument\n"); + THREADS_ALLOW(); + res = SSL_write(CON, sp[-args].u.string->str, sp[-args].u.string->len); + THREADS_DISALLOW(); + pop_n_elems(args); + push_int(res); +} + +void ssleay_connection_werror(INT32 args) +{ + ERR_print_errors_fp(stderr); + pop_n_elems(args); +} + +/* Methods for ssleay context objects */ + +static void ssleay_create(INT32 args) +{ + if (CTX) + SSL_CTX_free(CTX); + CTX = SSL_CTX_new(); + if (!CTX) + error("ssleay->create: couldn't allocate new ssl context\n"); + pop_n_elems(args); +} + +static void ssleay_use_certificate_file(INT32 args) +{ + if (sp[-args].type != T_STRING) + error("ssleay->use_certificate_file: wrong type"); + if (SSL_CTX_use_certificate_file(CTX, sp[-args].u.string->str, SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + error("ssleay->use_certificate_file: unable to use certificate"); + } + pop_n_elems(args); +} + +static void ssleay_use_private_key_file(INT32 args) +{ + if (sp[-args].type != T_STRING) + error("ssleay->use_private_key_file: wrong type"); + if (SSL_CTX_use_PrivateKey_file(CTX, sp[-args].u.string->str, SSL_FILETYPE_PEM) <= 0) + { + ERR_print_errors_fp(stderr); + error("ssleay->use_private_key_file: unable to use private_key\n"); + } + pop_n_elems(args); +} + +/* open(int fd, string mode) where mode is "s" for server + * or "c" for client */ +static void ssleay_new(INT32 args) +{ + struct object *res; + +#if 0 + if (strcmp(sp[-args+1].u.string->str, "s") == 0) + is_server = 1; + else + { + if (strcmp(sp[-args+1].u.string->str, "c") == 0) + error("ssleay->open: client mode not implemented\n") + else + error("ssleay->open: invalid mode\n"); + } +#endif + pop_n_elems(args); + THISOBJ->refs++; + push_object(THISOBJ); + push_object(clone(ssleay_connection_program, 1)); +} + + +/* Thread stuff */ +#ifdef _REENTRANT + +static MUTEX_T ssleay_locks[CRYPTO_NUM_LOCKS]; + +static void ssleay_locking_callback(int mode, int type, char *file, int line) +{ + if (mode & CRYPTO_LOCK) + mt_lock(ssleay_locks + type); + else + mt_unlock(ssleay_locks + type); +} + +static unsigned long ssleay_thread_id(void) +{ + return th_self(); +} + +static void ssleay_init_threads() +{ + int i; + for (i = 0; i<CRYPTO_NUM_LOCKS; i++) + mt_init(ssleay_locks + i); + CRYPTO_set_id_callback(ssleay_thread_id); + CRYPTO_set_locking_callback(ssleay_locking_callback); +} +#endif /* _REENTRANT */ + +/* Initializing etc */ +void init_context(struct object *o) +{ + CTX = NULL; +} + +void exit_context(struct object *o) +{ + if (CTX) + SSL_CTX_free(CTX); +} + +void init_connection(struct object *o) +{ + CON = NULL; +} + +void exit_connection(struct object *o) +{ + if (CON) + SSL_free(CON); +} + +#endif /* HAVE_SSLEAY */ + +void pike_module_exit() +{ +#ifdef HAVE_SSLEAY + free_program(ssleay_connection_program); + free_program(ssleay_program); + ssleay_connection_program=0; + ssleay_program=0; +#endif +} + +void pike_module_init(void) +{ +#ifdef HAVE_SSLEAY + ERR_load_ERR_strings(); + ERR_load_SSL_strings(); + ERR_load_crypto_strings(); +#ifdef _REENTRANT + ssleay_init_threads(); +#endif /* _REENTRANT */ + start_new_program(); + add_storage(sizeof(struct ssleay_context)); + + add_function("create", ssleay_create, "function(void:void)",0); + add_function("use_certificate_file", ssleay_use_certificate_file, "function(string:void)", 0); + add_function("use_private_key_file", ssleay_use_private_key_file, "function(string:void)", 0); + add_function("new", ssleay_new, "function(void:object)", 0); + + set_init_callback(init_context); + set_exit_callback(exit_context); + + ssleay_program=end_program(); + add_program_constant("ssleay",ssleay_program); + + start_new_program(); + add_storage(sizeof(struct ssleay_connection)); + + add_function("create", ssleay_connection_create, "function(object:void)",0); + add_function("accept", ssleay_connection_accept, "function(void:int)", 0); + add_function("read",ssleay_connection_read,"function(int,int|void:int|string)",0); + add_function("write",ssleay_connection_write,"function(string:int)",0); + add_function("set_fd",ssleay_connection_set_fd,"function(int:void)",0); + add_function("ssleay_werror", ssleay_connection_werror, "function(void:void)", 0); + set_init_callback(init_connection); + set_exit_callback(exit_connection); + + ssleay_connection_program=end_program(); + add_program_constant("connection",ssleay_program); +#endif /* HAVE_SSLEAY */ +} + + diff --git a/src/modules/call_out/call_out.c b/src/modules/call_out/call_out.c index f1cc05f6080713e135ffd28877c901c206343894..7c02d4db4a85ec338a1e7e4fdbd53804e5062896 100644 --- a/src/modules/call_out/call_out.c +++ b/src/modules/call_out/call_out.c @@ -4,7 +4,7 @@ ||| See the files COPYING and DISCLAIMER for more information. \*/ #include "global.h" -RCSID("$Id: call_out.c,v 1.6 1997/02/07 01:39:27 hubbe Exp $"); +RCSID("$Id: call_out.c,v 1.7 1997/02/11 08:39:34 hubbe Exp $"); #include "array.h" #include "dynamic_buffer.h" #include "object.h" @@ -32,11 +32,20 @@ struct call_out_s typedef struct call_out_s call_out; -call_out **pending_calls=0; /* pointer to first busy pointer */ int num_pending_calls; /* no of busy pointers in buffer */ -static call_out **call_buffer=0; /* pointer to buffer */ +static call_out *call_buffer=0; /* pointer to buffer */ static int call_buffer_size; /* no of pointers in buffer */ +#undef CAR +#undef CDR + +#define CAR(X) (((X)<<1)+1) +#define CDR(X) (((X)<<1)+2) +#define PARENT(X) (((X)-1)>>1) +#define CALL(X) call_buffer[(X)] +#define CMP(X,Y) my_timercmp(& CALL(X).tv, <, & CALL(Y).tv) +#define SWAP(X,Y) do{ call_out _tmp=CALL(X); CALL(X)=CALL(Y); CALL(Y)=_tmp; } while(0) + static void verify_call_outs() { #ifdef DEBUG @@ -49,18 +58,15 @@ static void verify_call_outs() if(num_pending_calls<0 || num_pending_calls>call_buffer_size) fatal("Error in call out tables.\n"); - if(pending_calls+num_pending_calls!=call_buffer+call_buffer_size) - fatal("Error in call out tables.\n"); - for(e=0;e<num_pending_calls;e++) { if(e) { - if(my_timercmp(&pending_calls[e-1]->tv,>,&pending_calls[e]->tv)) - fatal("Error in call out order.\n"); + if(CMP(e, PARENT(e))) + fatal("Error in call out heap.\n"); } - if(!(v=pending_calls[e]->args)) + if(!(v=CALL(e).args)) fatal("No arguments to call\n"); if(v->refs < 1) @@ -72,6 +78,54 @@ static void verify_call_outs() #endif } +static void adjust_down(int pos) +{ + while(1) + { + int a=CAR(pos), b=CDR(pos); + if(a >= num_pending_calls) break; + if(b < num_pending_calls) + if(CMP(b, a)) + a=b; + + if(CMP(pos, a)) break; + SWAP(pos, a); + pos=a; + } +} + +static int adjust_up(int pos) +{ + int parent=PARENT(pos); + int from; + if(!pos) return 0; + + if(CMP(pos, parent)) + { + SWAP(pos, parent); + from=pos; + pos=parent; + while(pos && CMP(pos, parent=PARENT(pos))) + { + SWAP(pos, parent); + from=pos; + pos=parent; + } + from^=1; + if(from < num_pending_calls && CMP(from, pos)) + { + SWAP(from, pos); + adjust_down(from); + } + return 1; + } + return 0; +} + +static void adjust(int pos) +{ + if(!adjust_up(pos)) adjust_down(pos); +} /* start a new call out, return 1 for success */ static struct array * new_call_out(int num_arg,struct svalue *argp) @@ -79,40 +133,31 @@ static struct array * new_call_out(int num_arg,struct svalue *argp) int e,c; call_out *new,**p,**pos; - if(!call_buffer) - { - call_buffer_size=20; - call_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size); - if(!call_buffer) return 0; - pending_calls=call_buffer+call_buffer_size; - num_pending_calls=0; - } - if(num_pending_calls==call_buffer_size) { /* here we need to allocate space for more pointers */ - call_out **new_buffer; - - new_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size*2); - if(!new_buffer) - return 0; - - MEMCPY((char *)(new_buffer+call_buffer_size), - (char *)call_buffer, - sizeof(call_out *)*call_buffer_size); - free((char *)call_buffer); - call_buffer=new_buffer; - pending_calls=call_buffer+call_buffer_size; - call_buffer_size*=2; + call_out *new_buffer; + + if(!call_buffer) + { + call_buffer_size=128; + call_buffer=(call_out *)xalloc(sizeof(call_out)*call_buffer_size); + if(!call_buffer) return 0; + num_pending_calls=0; + }else{ + new_buffer=(call_out *)realloc((char *)call_buffer, sizeof(call_out)*call_buffer_size*2); + if(!new_buffer) + error("Not enough memorry for another call_out\n"); + call_buffer_size*=2; + call_buffer=new_buffer; + } } /* time to allocate a new call_out struct */ f_aggregate(num_arg-1); - new=(call_out *)xalloc(sizeof(call_out)); + new=&CALL(num_pending_calls); - if(!new) return 0; - if(argp[0].type==T_INT) { new->tv.tv_sec=argp[0].u.integer; @@ -142,29 +187,9 @@ static struct array * new_call_out(int num_arg,struct svalue *argp) new->args=sp[-1].u.array; sp -= 2; - /* time to link it into the buffer using binsearch */ - pos=pending_calls; - - e=num_pending_calls; - while(e>0) - { - c=e/2; - if(my_timercmp(& new->tv,>,& pos[c]->tv)) - { - pos+=c+1; - e-=c+1; - }else{ - e=c; - } - } - pos--; - pending_calls--; - for(p=pending_calls;p<pos;p++) p[0]=p[1]; - *pos=new; num_pending_calls++; - + adjust_up(num_pending_calls-1); verify_call_outs(); - return new->args; } @@ -223,18 +248,18 @@ void do_call_outs(struct callback *ignored, void *ignored_too, void *arg) { tmp=(time_t)TIME(0); while(num_pending_calls && - my_timercmp(&pending_calls[0]->tv,<=,¤t_time)) + my_timercmp(&CALL(0).tv,<=,¤t_time)) { /* unlink call out */ - c=pending_calls[0]; - pending_calls++; - num_pending_calls--; + call_out c; + c=CALL(0); + CALL(0)=CALL(--num_pending_calls); + adjust_down(0); - if(c->caller) free_object(c->caller); + if(c.caller) free_object(c.caller); - args=c->args->size; - push_array_items(c->args); - free((char *)c); + args=c.args->size; + push_array_items(c.args); check_destructed(sp-args); if(sp[-args].type!=T_INT) { @@ -252,15 +277,15 @@ void do_call_outs(struct callback *ignored, void *ignored_too, void *arg) { if(num_pending_calls) { - if(my_timercmp(& pending_calls[0]->tv, < , &next_timeout)) + if(my_timercmp(& CALL(0).tv, < , &next_timeout)) { - next_timeout = pending_calls[0]->tv; + next_timeout = CALL(0).tv; } }else{ if(call_out_backend_callback) { /* There are no call outs queued, let's remove - * the taxing backend callback... + * the "taxing" backend callback... */ remove_callback(call_out_backend_callback); call_out_backend_callback=0; @@ -275,11 +300,11 @@ static int find_call_out(struct svalue *fun) if(fun->type == T_ARRAY) for(e=0;e<num_pending_calls;e++) - if(pending_calls[e]->args == fun->u.array) + if(CALL(e).args == fun->u.array) return e; for(e=0;e<num_pending_calls;e++) - if(is_eq(fun, ITEM(pending_calls[e]->args))) + if(is_eq(fun, ITEM(CALL(e).args))) return e; return -1; @@ -298,7 +323,7 @@ void f_find_call_out(INT32 args) sp->u.integer=-1; sp++; }else{ - push_int(pending_calls[e]->tv.tv_sec - current_time.tv_sec); + push_int(CALL(e).tv.tv_sec - current_time.tv_sec); } verify_call_outs(); } @@ -311,15 +336,12 @@ void f_remove_call_out(INT32 args) if(e!=-1) { pop_n_elems(args); - push_int(pending_calls[e]->tv.tv_sec - current_time.tv_sec); - free_array(pending_calls[e]->args); - if(pending_calls[e]->caller) - free_object(pending_calls[e]->caller); - free((char*)(pending_calls[e])); - for(;e>0;e--) - pending_calls[e]=pending_calls[e-1]; - pending_calls++; - num_pending_calls--; + push_int(CALL(e).tv.tv_sec - current_time.tv_sec); + free_array(CALL(e).args); + if(CALL(e).caller) + free_object(CALL(e).caller); + CALL(e)=CALL(--num_pending_calls); + adjust(e); }else{ pop_n_elems(args); sp->type=T_INT; @@ -343,22 +365,23 @@ struct array *get_all_call_outs() for(e=0;e<num_pending_calls;e++) { struct array *v; - v=allocate_array_no_init(pending_calls[e]->args->size+2, 0); + v=allocate_array_no_init(CALL(e).args->size+2, 0); ITEM(v)[0].type=T_INT; ITEM(v)[0].subtype=NUMBER_NUMBER; - ITEM(v)[0].u.integer=pending_calls[e]->tv.tv_sec - current_time.tv_sec; + ITEM(v)[0].u.integer=CALL(e).tv.tv_sec - current_time.tv_sec; - if(pending_calls[e]->caller) + if(CALL(e).caller) { ITEM(v)[1].type=T_OBJECT; - (ITEM(v)[1].u.object=pending_calls[e]->caller) ->refs++; + ITEM(v)[1].u.object=CALL(e).caller; + CALL(e).caller->refs++; }else{ ITEM(v)[1].type=T_INT; ITEM(v)[1].subtype=NUMBER_NUMBER; ITEM(v)[1].u.integer=0; } - assign_svalues_no_free(ITEM(v)+2,ITEM(pending_calls[e]->args),pending_calls[e]->args->size,BIT_MIXED); + assign_svalues_no_free(ITEM(v)+2,ITEM(CALL(e).args),CALL(e).args->size,BIT_MIXED); ITEM(ret)[e].type=T_ARRAY; ITEM(ret)[e].u.array=v; @@ -378,14 +401,12 @@ void free_all_call_outs() verify_call_outs(); for(e=0;e<num_pending_calls;e++) { - free_array(pending_calls[e]->args); - if(pending_calls[e]->caller) free_object(pending_calls[e]->caller); - free((char*)(pending_calls[e])); + free_array(CALL(e).args); + if(CALL(e).caller) free_object(CALL(e).caller); } if(call_buffer) free((char*)call_buffer); num_pending_calls=0; call_buffer=NULL; - pending_calls=NULL; } #ifdef DEBUG diff --git a/src/modules/files/testsuite.in b/src/modules/files/testsuite.in index 168cdeead3617b84b14717ef0f220dee82310cd5..70959e39ae89ca1a1f1caa544daaf9fd2e8e87db 100644 --- a/src/modules/files/testsuite.in +++ b/src/modules/files/testsuite.in @@ -1,12 +1,12 @@ // tests for file module -test_true(programp((program)"/precompiled/file")) -test_true(programp(File)) -test_true(programp((program)"/precompiled/port")) -test_any(object o; o=clone(File); destruct(o); return 1,1) +test_true(programp(Stdio.File)) +test_true(programp(Stdio.File)) +test_true(programp(Stdio.Port)) +test_any(object o; o=clone(Stdio.File); destruct(o); return 1,1) // - file->open // - file->close -test_any(object o=clone(File); return o->open("conftest","wct") && o->close(),1) +test_any(object o=clone(Stdio.File); return o->open("conftest","wct") && o->close(),1) // - file_stat test_eq(file_stat("conftest")[1],0) @@ -16,59 +16,59 @@ test_true(rm("conftest")) test_eq(file_stat("conftest"),0) // - file->write -test_any(int e; object o=clone(File); if(!o->open("conftest","wct")) return -1; e=o->write("sune"); if(!o->close()) return -1; return e,4) +test_any(int e; object o=clone(Stdio.File); if(!o->open("conftest","wct")) return -1; e=o->write("sune"); if(!o->close()) return -1; return e,4) // - file->read -test_any(string s; object o=clone(File); if(!o->open("conftest","r")) return -1; s=o->read(4); if(!o->close()) return -1; return s,"sune") +test_any(string s; object o=clone(Stdio.File); if(!o->open("conftest","r")) return -1; s=o->read(4); if(!o->close()) return -1; return s,"sune") -test_any(string s; object o=clone(File); if(!o->open("conftest","r")) return -1; s=o->read(999999); if(!o->close()) return -1; return s,"sune") +test_any(string s; object o=clone(Stdio.File); if(!o->open("conftest","r")) return -1; s=o->read(999999); if(!o->close()) return -1; return s,"sune") -test_any(int e; object o=clone(File); if(!o->open("conftest","wct")) return -1; e=o->write(sprintf("%'+-*'100000s","")); if(!o->close()) return -1; return e,100000) +test_any(int e; object o=clone(Stdio.File); if(!o->open("conftest","wct")) return -1; e=o->write(sprintf("%'+-*'100000s","")); if(!o->close()) return -1; return e,100000) -test_any(string s; object o=clone(File); if(!o->open("conftest","r")) return -1; s=o->read(9999999); if(!o->close()) return -1; return s,sprintf("%'+-*'100000s","")) +test_any(string s; object o=clone(Stdio.File); if(!o->open("conftest","r")) return -1; s=o->read(9999999); if(!o->close()) return -1; return s,sprintf("%'+-*'100000s","")) // - file->seek // - file->tell -test_any(object o=clone(File); return o->open("conftest","r") && o->read(4711) && o->tell() == 4711 && o->close(),1) +test_any(object o=clone(Stdio.File); return o->open("conftest","r") && o->read(4711) && o->tell() == 4711 && o->close(),1) // - file->stat -test_any(object o=clone(File); return equal(o->open("conftest","r") && o->stat(), file_stat("conftest")),1) +test_any(object o=clone(Stdio.File); return equal(o->open("conftest","r") && o->stat(), file_stat("conftest")),1) // - file->errno -test_do(clone(File,"stdin")->errno()) +test_do(clone(Stdio.File,"stdin")->errno()) // - file->set_nonblocking // - file->set_blocking // - file->set_id // - file->query_id -test_false(clone(File,"stdin")->query_id()) +test_false(clone(Stdio.File,"stdin")->query_id()) // - File->query_read_callback -test_do(clone(File,"stdin")->query_read_callback()) +test_do(clone(Stdio.File,"stdin")->query_read_callback()) // - file->query_write_callback -test_do(clone(File,"stdin")->query_write_callback()) +test_do(clone(Stdio.File,"stdin")->query_write_callback()) // - file->query_close_callback -test_do(clone(File,"stdin")->query_close_callback()) +test_do(clone(Stdio.File,"stdin")->query_close_callback()) // - file->open_socket // - file->connect // - file->query_address // - file->pipe -test_any([[object o=clone(File),o2=o->pipe();o->write("1"); return o2->read(1)]],"1") -test_any([[object o=clone(File),o2=o->pipe();o2->write("1"); return o->read(1)]],"1") +test_any([[object o=clone(Stdio.File),o2=o->pipe();o->write("1"); return o2->read(1)]],"1") +test_any([[object o=clone(Stdio.File),o2=o->pipe();o2->write("1"); return o->read(1)]],"1") // - file->dup -test_any([[object o=clone(File); o->open("conftest","r"); o=o->dup(); return o->read(100)]] ,sprintf("%'+-*'100s","")) +test_any([[object o=clone(Stdio.File); o->open("conftest","r"); o=o->dup(); return o->read(100)]] ,sprintf("%'+-*'100s","")) // - file->assign -est_any([[object o=clone(File),o2=clone(File); o->open("conftest","r"); o2->assign(o); return o2->read(100)]] ,sprintf("%'+-*'100s","")) +est_any([[object o=clone(Stdio.File),o2=clone(Stdio.File); o->open("conftest","r"); o2->assign(o); return o2->read(100)]] ,sprintf("%'+-*'100s","")) // - file->dup2 -test_any([[object o=clone(File),o2=clone(File); o2->pipe(); o->open("conftest","r"); o->dup2(o2); return o2->read(100)]] ,sprintf("%'+-*'100s","")) +test_any([[object o=clone(Stdio.File),o2=clone(Stdio.File); o2->pipe(); o->open("conftest","r"); o->dup2(o2); return o2->read(100)]] ,sprintf("%'+-*'100s","")) -test_eq(popen("echo foo"),"foo\n") +test_eq(Process.popen("echo foo"),"foo\n") // - socket->bind // - socket->set_id @@ -92,29 +92,25 @@ test_true(stringp(getcwd())) test_eq('/',getcwd()[0]) // strerror -cond([[all_efuns()->strerror]], +cond([[all_constants()->strerror]], [[ test_do(strerror(1)) test_true(stringp(strerror(2)||"")) ]]) -test_do(object o=clone(File); if(!o->open("conftest","wct")) return -1; o->write(strmult("foo\n",100)); o->close();) - -test_any([[ -#include <stdio.h> -return 0]],0) +test_do(object o=clone(Stdio.File); if(!o->open("conftest","wct")) return -1; o->write(String.strmult("foo\n",100)); o->close();) // /precompiled/FILE -test_any([[object o=clone(FILE); o->open("conftest","r"); return o->gets()]],"foo") -test_any(object o=clone(FILE); o->open("conftest","r"); return o->gets()+o->gets()+o->gets(),"foofoofoo") -test_any(int e; object o=clone(FILE); o->open("conftest","r"); for(e=0;e<100;e++) if(o->gets() != "foo") return e; return -1,-1) +test_any([[object o=clone(Stdio.FILE); o->open("conftest","r"); return o->gets()]],"foo") +test_any(object o=clone(Stdio.FILE); o->open("conftest","r"); return o->gets()+o->gets()+o->gets(),"foofoofoo") +test_any(int e; object o=clone(Stdio.FILE); o->open("conftest","r"); for(e=0;e<100;e++) if(o->gets() != "foo") return e; return -1,-1) -test_true(stdin) -test_true(stdout) -test_true(stderr) +test_true(Stdio.stdin) +test_true(Stdio.stdout) +test_true(Stdio.stderr) -test_eq(read_file("conftest",0,5),strmult("foo\n",5)) -test_eq(read_file("conftest",1,5),strmult("foo\n",5)) -test_eq(read_file("conftest",100,5),"") +test_eq(Stdio.read_file("conftest",0,5),String.strmult("foo\n",5)) +test_eq(Stdio.read_file("conftest",1,5),String.strmult("foo\n",5)) +test_eq(Stdio.read_file("conftest",100,5),"") test_do(rm("conftest")) \ No newline at end of file diff --git a/src/modules/readline/.cvsignore b/src/modules/readline/.cvsignore new file mode 100644 index 0000000000000000000000000000000000000000..74ee34e458820cc9a20357e972f33acb4b423f03 --- /dev/null +++ b/src/modules/readline/.cvsignore @@ -0,0 +1,10 @@ +.pure +Makefile +config.log +config.status +configure +dependencies +lib_dirs +linker_options +readline_machine.h +stamp-h diff --git a/src/modules/readline/.gitignore b/src/modules/readline/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..85b1cf5830cd9ca112606ef69527ffa8b619bbbf --- /dev/null +++ b/src/modules/readline/.gitignore @@ -0,0 +1,10 @@ +/.pure +/Makefile +/config.log +/config.status +/configure +/dependencies +/lib_dirs +/linker_options +/readline_machine.h +/stamp-h diff --git a/src/modules/readline/Makefile.in b/src/modules/readline/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..d16c08f3ad4f3ceeb4f81f7becad5a073452eff9 --- /dev/null +++ b/src/modules/readline/Makefile.in @@ -0,0 +1,8 @@ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +MODULE_CPPFLAGS=@CPPFLAGS@ +MODULE_LDFLAGS=@LDFLAGS@ @LIBS@ +OBJS=readlinemod.o + +@dynamic_module_makefile@ +@dependencies@ diff --git a/src/modules/readline/configure.in b/src/modules/readline/configure.in new file mode 100644 index 0000000000000000000000000000000000000000..2d27610c9c5ff16709debbe3e480da6bd5f4ae60 --- /dev/null +++ b/src/modules/readline/configure.in @@ -0,0 +1,52 @@ +AC_INIT(readlinemod.c) +AC_CONFIG_HEADER(readline_machine.h) +AC_ARG_WITH(readline,[ --with(out)-readline support command line editing],[],[with_readline=yes]) + +sinclude(../module_configure.in) + +if test x$with_readline = xyes ; then + AC_MSG_CHECKING(Checking for GNU directory) + + AC_CACHE_VAL(pike_cv_gnu_dir, [ + for pike_cv_gnu_dir in /usr/gnu /opt/gnu /usr/local/gnu /sw/gnu no; do + if test -d $pike_cv_gnu_dir/. ; then + break + else + : + fi + done + ]) + AC_MSG_RESULT($pike_cv_gnu_dir) + + if test x$pike_cv_gnu_dir != xno; then + if test -d $pike_cv_gnu_dir/include/. ; then + echo Adding $pike_cv_gnu_dir/include to the include path + CPPFLAGS="$CPPFLAGS -I$pike_cv_gnu_dir/include" + else + : + fi + if test -d $pike_cv_gnu_dir/lib/. ; then + echo Adding $pike_cv_gnu_dir/lib to the runtime link path + echo $pike_cv_gnu_dir/lib >lib_dirs + elif test -f lib_dirs ; then + rm lib_dirs + else + : + fi + else + : + fi + + AC_CHECK_HEADERS(readline.h history.h readline/readline.h history/history.h readline/history.h) + + if test $ac_cv_header_readline_h = yes -o $ac_cv_header_readline_readline_h = yes ; then + if test $ac_cv_header_history_h = yes -o $ac_cv_header_history_history_h = yes -o $ac_cv_header_readline_history_h = yes ; then + AC_CHECK_LIB(termcap, tputs) + AC_CHECK_LIB(readline, readline) + fi + fi +fi + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) + + diff --git a/src/modules/readline/doc/readline b/src/modules/readline/doc/readline new file mode 100644 index 0000000000000000000000000000000000000000..01f3204c02d443f207edaa5b1348f320f1031c28 --- /dev/null +++ b/src/modules/readline/doc/readline @@ -0,0 +1,14 @@ +NAME + readline - read a line from stdin + +SYNTAX + string readline(string prompt); + +DESCRIPTION + This function writes the string 'prompt' and then waits until the + user has entered a line from the keyboard. If the readline library + was available when Pike was compiled the user will have history and + line edithing at his/her disposal when entering the line. + +SEE ALSO + files/file diff --git a/src/modules/readline/readline_machine.h.in b/src/modules/readline/readline_machine.h.in new file mode 100644 index 0000000000000000000000000000000000000000..7ce5c2ec3ae6a7ec83311f5c10fc614513a3f908 --- /dev/null +++ b/src/modules/readline/readline_machine.h.in @@ -0,0 +1,19 @@ +#ifndef GDBM_MACHINE_H +#define GDBM_MACHINE_H + +/* Define this if you have <readline.h> */ +#undef HAVE_READLINE_H +#undef HAVE_READLINE_READLINE_H + +/* Define this if you have <history.h> */ +#undef HAVE_HISTORY_H +#undef HAVE_HISTORY_HISTORY_H +#undef HAVE_READLINE_HISTORY_H + +/* Define this if you have -lreadline */ +#undef HAVE_LIBREADLINE + +/* Define this if you have -ltermcap */ +#undef HAVE_LIBTERMCAP + +#endif diff --git a/src/modules/readline/readlinemod.c b/src/modules/readline/readlinemod.c new file mode 100644 index 0000000000000000000000000000000000000000..ceaa4ed132e6e1300e49248d34dbc5dca28a3e83 --- /dev/null +++ b/src/modules/readline/readlinemod.c @@ -0,0 +1,140 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#include "global.h" +#include "readline_machine.h" +#include "types.h" +#include "interpret.h" +#include "svalue.h" +#include "stralloc.h" +#include "array.h" +#include "object.h" +#include "macros.h" +#include "threads.h" + +#ifndef HAVE_LIBTERMCAP +#undef HAVE_LIBREADLINE +#endif + +#if !defined(HAVE_READLINE_H) && !defined(HAVE_READLINE_READLINE_H) +#undef HAVE_LIBREADLINE +#endif + +#if !defined(HAVE_HISTORY_H) && !defined(HAVE_READLINE_HISTORY_H) && !defined(HAVE_HISTORY_HISTORY_H) +#undef HAVE_LIBREADLINE +#endif + +#ifdef HAVE_LIBREADLINE + +#ifdef HAVE_READLINE_READLINE_H +#include <readline/readline.h> +#else +#ifdef HAVE_READLINE_H +#include <readline.h> +#endif +#endif + +#ifdef HAVE_READLINE_HISTORY_H +#include <readline/history.h> +#else +#ifdef HAVE_HISTORY_HISTORY_H +#include <history/history.h> +#else +#ifdef HAVE_HISTORY_H +#include <history.h> +#endif +#endif +#endif + +static void f_readline(INT32 args) +{ + char *r; + struct pike_string *str; + if(args < 1) + error("Too few arguments to readline().\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to readline()\n"); + + str=sp[-args].u.string; + THREADS_ALLOW(); + r=readline(str->str); + THREADS_DISALLOW(); + + pop_n_elems(args); + if(r) + { + if(*r) add_history(r); + push_string(make_shared_string(r)); + free(r); + } else { + push_int(0); + } +} + +void pike_module_init(void) +{ + rl_bind_key('\t', rl_insert); + add_function_constant("_module_value",f_readline,"function(string:string)",OPT_SIDE_EFFECT); +} + +#else + +#include <stdio.h> + +#define BLOCK 16384 + +static void f_readline(INT32 args) +{ + char *prompt; + int plen; + char line[BLOCK]; + char *r; + int tmp; + + if(args < 1) + error("Too few arguments to readline().\n"); + + if(sp[-args].type != T_STRING) + error("Bad argument 1 to readline()\n"); + + prompt = sp[-args].u.string->str; + plen = sp[-args].u.string->len; + + THREADS_ALLOW(); + + write(1, prompt, plen); + r=fgets(line,BLOCK-1,stdin); /* Should probably get rid of this one */ + line[BLOCK-1] = '\0'; /* Always NUL-terminated */ + + THREADS_DISALLOW(); + + pop_n_elems(args); + + if (r) + { + INT32 len; + if ((len=strlen(line))) + { + if (line[len-1]=='\n') + { + push_string(make_shared_binary_string(line,len-1)); + return; + } + } + } + push_int(0); +} + +void pike_module_init(void) +{ + add_function_constant("_module_value",f_readline,"function(string:string)",OPT_SIDE_EFFECT); +} + +#endif + +void pike_module_exit(void) {} + + diff --git a/src/modules/readline/testsuite.in b/src/modules/readline/testsuite.in new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391