Initial revision Imager-0_38 Imager-0_38pre9
authorArnar Mar Hrafnkelsson <addi@cpan.org>
Sun, 25 Mar 2001 11:54:59 +0000 (11:54 +0000)
committerArnar Mar Hrafnkelsson <addi@cpan.org>
Sun, 25 Mar 2001 11:54:59 +0000 (11:54 +0000)
95 files changed:
Changes [new file with mode: 0644]
Imager.pm [new file with mode: 0644]
Imager.xs [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
README [new file with mode: 0644]
bench/benchform.perl [new file with mode: 0644]
bench/kscdisplay.png [new file with mode: 0644]
bench/makegrad.perl [new file with mode: 0644]
bench/quantbench.perl [new file with mode: 0644]
bench/quantone.perl [new file with mode: 0644]
bigtest.perl [new file with mode: 0644]
conv.c [new file with mode: 0644]
datatypes.c [new file with mode: 0644]
datatypes.h [new file with mode: 0644]
doco.perl [new file with mode: 0755]
draw.c [new file with mode: 0644]
draw.h [new file with mode: 0644]
dynaload.c [new file with mode: 0644]
dynaload.h [new file with mode: 0644]
dynfilt/Makefile.PL [new file with mode: 0644]
dynfilt/compile.txt [new file with mode: 0644]
dynfilt/dt2.c [new file with mode: 0644]
dynfilt/dyntest.c [new file with mode: 0644]
dynfilt/flines.c [new file with mode: 0644]
dynfilt/mandelbrot.c [new file with mode: 0644]
dynfilt/pluginst.h [new file with mode: 0644]
errep.perl [new file with mode: 0644]
ext.h [new file with mode: 0644]
feat.c [new file with mode: 0644]
feat.h [new file with mode: 0644]
filterlist.perl [new file with mode: 0755]
filters.c [new file with mode: 0644]
font.c [new file with mode: 0644]
fontfiles/dcr10.afm [new file with mode: 0644]
fontfiles/dcr10.pfb [new file with mode: 0644]
fontfiles/dodge.ttf [new file with mode: 0644]
gaussian.c [new file with mode: 0644]
gif.c [new file with mode: 0644]
image.c [new file with mode: 0644]
image.h [new file with mode: 0644]
imio.h [new file with mode: 0644]
io.c [new file with mode: 0644]
iolayer.c [new file with mode: 0644]
iolayer.h [new file with mode: 0644]
jpeg.c [new file with mode: 0644]
lib/Imager/Color.pm [new file with mode: 0644]
lib/Imager/Expr.pm [new file with mode: 0644]
lib/Imager/Expr/Assem.pm [new file with mode: 0644]
lib/Imager/Font.pm [new file with mode: 0644]
lib/Imager/Regops.pm [new file with mode: 0644]
lib/Imager/Transform.pm [new file with mode: 0644]
lib/Imager/regmach.pod [new file with mode: 0644]
log.c [new file with mode: 0644]
log.h [new file with mode: 0644]
plug.h [new file with mode: 0644]
png.c [new file with mode: 0644]
pnm.c [new file with mode: 0644]
ppport.h [new file with mode: 0644]
quant.c [new file with mode: 0644]
raw.c [new file with mode: 0644]
regmach.c [new file with mode: 0644]
regmach.h [new file with mode: 0644]
regops.perl [new file with mode: 0644]
spot.perl [new file with mode: 0644]
stackmach.c [new file with mode: 0644]
stackmach.h [new file with mode: 0644]
t/t00basic.t [new file with mode: 0644]
t/t10formats.t [new file with mode: 0644]
t/t15color.t [new file with mode: 0644]
t/t30t1font.t [new file with mode: 0644]
t/t35ttfont.t [new file with mode: 0644]
t/t36oofont.t [new file with mode: 0644]
t/t40scale.t [new file with mode: 0644]
t/t50basicoo.t [new file with mode: 0644]
t/t55trans.t [new file with mode: 0644]
t/t56postfix.t [new file with mode: 0644]
t/t57infix.t [new file with mode: 0644]
t/t58trans2.t [new file with mode: 0644]
t/t59assem.t [new file with mode: 0644]
t/t60dyntest.t [new file with mode: 0644]
t/t65crop.t [new file with mode: 0644]
t/t66paste.t [new file with mode: 0644]
t/t70newgif.t [new file with mode: 0644]
t/t75polyaa.t [new file with mode: 0644]
t/t90cc.t [new file with mode: 0644]
testimg/penguin-base.ppm [new file with mode: 0644]
testimg/scale.gif [new file with mode: 0644]
testimg/scale.ppm [new file with mode: 0644]
testimg/scalei.gif [new file with mode: 0644]
tiff.c [new file with mode: 0644]
trans2.c [new file with mode: 0644]
transbench.perl [new file with mode: 0644]
transform.perl [new file with mode: 0644]
typemap [new file with mode: 0644]

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..c81bd87
--- /dev/null
+++ b/Changes
@@ -0,0 +1,404 @@
+Revision history for Perl extension Imager.
+
+0.01  Thu May  6 15:25:03 1999
+       - original version; created by h2xs 1.18
+
+0.02  Mon May 10 20:20:20 1999
+        - Added PPM writer, and a new return type to
+       be used to return undef() from a function returning
+       (int)0.
+
+0.03  Thu May 20 23:23:23 1999
+        - Added Pseudo image handling thingies, now
+       you can use your own get and put pixel routines.
+
+0.04  Mon May 24 22:14:55 1999
+        - Rewrote parts of jpeg support.
+
+0.05  Tue May 25 23:40:01 1999
+       - Added png support with libpng
+       fixed error reporting and return types from
+       some format routines.
+
+0.06  Wed May 26 19:05:39 1999
+        - Fixed Data::Dumper being used when it shouldn't have
+       added feature checking to see at run time if 
+       certain formats are avaliable.
+       removed some ancient C++ style comments that hadn't 
+       been properly removed, some out of bounds errors in ppm.c
+       fixed tests so missing features are skipped.
+
+0.07  Thu May 27 03:15:00 1999
+        - Fixed the typemap for i_img and i_color so that you can pass
+       a null pointer to them without getting a warning when warnings
+       are enabled. Maybe this is counter perl but it greatly increases
+       the usability of the functions.
+
+0.08   Fri May 28 14:13:21 1999
+         - Added support for gif via, giflib or libungif.
+        gif is so ridden with patent issues but the user
+        can decide which library to use since they have the same
+        include files and calling interface.
+
+0.09   Mon May 31 17:52:32 1999
+         - Added image scaling per axis - faster than doing both
+        axes at the same time.  The current method uses lancoz
+        kernel and filtering. But more types should be added -
+        including: nearest neighbor, linear, and bicubic.
+
+0.10   Mon Jun  7 03:25:54 1999
+         - Added T1 postscript font handling. It's very primitive
+        at the moment but creative people can do nice stuff 
+        with it.  Currently it is not possible to have it 
+        generate an image of the correct size for a given string 
+        but that is on the way.
+
+0.11   Mon Jun  7 14:43:35 1999
+         - Added T1 features for direct to image rendering in 
+        a single color.  fixed some debug messages going to 
+        stderr instead of a log file.
+
+0.12   Tue Jun  8 02:58:43 1999
+         - Fixed bugs in jpeg.c when loading images. Also specified
+        prototype behaviour to on in the Imager.xs file. The
+        Makefile.PL step doesn't complain anymore so that is
+        hopefully fixed.
+
+0.13   Wed Jun  9 06:55:58 1999
+         - Fixed Imager.xs for init_log call.  Minor fixes here
+        and there.
+
+0.14   Thu Jun 10 14:11:22 1999
+         - Rewrote most of the logging stuff so that it is now
+        possible to remove all logging from the XS part of the
+        module.  Also some primitive check of memory usage was
+        added. Claes Jacobsson sent in some filters, inverter,
+        contrast and a noise filter.
+
+0.15   Mon Jun 14 08:13:29 1999
+         - Wrote minor enhancement on the calling syntax list.
+         Started on the object interface - added better support
+         for quering avaliable filetypes.  Fixed memory leaks in       
+         most loaders. New filters from Claes in this version
+         are bumpmap, postlevels and mosaic.
+
+0.16   Wed Jun 16 20:54:33 1999
+         - Added fixes to the BEGIN and END parts, added writer
+         function for the OO interface. Also added basic IPTC 
+         reading support for jpegs.  Also a few filters have been
+         added to the OO interface.
+
+0.17   Thu Jun 24 11:09:15 1999
+         - Added dynamic loading stuff - It's still missing a nice
+         global symbol table. This will be fixed in next release.
+         also calling the plugins is not all to easy at the moment.
+
+
+0.18   Mon Jun 28 12:31:33 1999
+         - Added global symbol table - plugins now need a symbol
+         table pointer in them. When the module is loaded it is
+         set to point at the global symbol table. Also some barebones
+         Makefile.PL has been made in the dynfilt directory - it works
+         on my system - I hope it does on yours.
+
+
+0.19   Fri Jul  1 15:00:03 1999        
+         - Added a way new scaling method for creating easy previews.
+         It only uses nearest neighbor, so it's doesn't look very nice
+         but it may be better for applications like remote sensing.
+
+0.20   Mon Jul  5 10:15:37 1999
+         - Added and rewrote documentation.
+
+0.21   Mon Jul  6 19:15:37 1999
+         - Fixed a bug in the scaling routine - it wasn't
+         handling 0< cases.
+
+0.22   Sat Oct  9 07:04:14 1999
+         - Added a new method to write gif images - now 
+         it is possible to have a part of a palette fixed.
+         this is very usefull if one needs to make sure that
+         some color like white is in the pallete.  This method
+         also allows some ditherding and gives better colormap
+         than the mediancut from the gif libraries.  It does
+         need much more cpu power though.  Hopefully later versions
+         will be faster.
+
+0.23   **************** Internal release only
+         - Fixed the %instances bug - caused ALL memory to be leaked.
+         Added real noise function - need feedback on how it should
+         be used. Also box(), and polyline are now in place.  Polygon
+         is missing but antialiased line drawing with integer endpoints are
+          done but should be replaced with a version that can have 
+         floating point endvalues. Two noise filters addded.
+
+0.24   **************** Internal release only
+         - Converted i_color into an object from a reference, so now it's
+       giving an object instead of a void ptr or an integer or something.
+
+0.25   **************** Internal release only
+         - Added basic Truetype functionality - still needs a rewrite
+       to be decent. Currently it's a port of a demo program that
+       uses an awful amount of global variables and there is much IO since
+       no caching of glyphs is done.
+
+0.26    Tue Nov 23 03:57:00 1999 > Development release <
+         - Added transformations so that an image can be wrapped.
+       To achive decent speed a C based stackmachine is included. As a result
+       transformations need to be specified in rpn (postfix) notation. It
+       also can use the Affix::Infix2Postfix class to do the conversion for it.
+
+0.27    Tue Dec 28 03:57:00 1999 > CPAN release <
+         - This is a bugfix version mostly, thanks to claes for pointing
+       out the problems - fixed palette saving wasn't working correctly after
+       version 0.24 - rather surprised this didn't crash everything.
+       Also fixed that for t1 fonts the bounding box wasn't being reported 
+       unless the font had been used before.  This is either a bug in t1lib
+       or a mistake in it's documentation.  Another lingering bug since 0.24
+       what that $img->box() wasn't creating it's default color properly.
+       Added i_tt_text() method and more debuging to the truetype routines.            
+       truetype testcase fixed and old debug rubish removed.
+
+0.28    Tue Jan  4 05:25:58 2000 > CPAN release <
+         - Only fixes to truetype test and transformation tests.
+       Thanks to schinder of cpan testers for testing and reporting.
+
+0.29    Tue Jan  4 21:49:57 2000 > CPAN release <
+         - fixes to get rid of warnings under pre 5.005, 
+       Fixed broken preproccessor directives to work on non gnu
+       compilers.  Fixed DSO test on HPUX - both code errors and
+       HPUX creates .sl instead of .so so the tests were failing.
+
+0.30    Sun Jan  7 05:00:06 2000 > Bunch of Alpha releases <
+         - An attempt to automate installation.
+
+0.31    Sat Jan 15 03:58:29 2000 > Fixes fixes fixes <
+         - Fixed a bug pointed out by Leolo where loading gifs
+       skips the first line of the imageload() has been
+       by read() - for now load is an alias for read.  It will
+       be removed in the future.  Also, fixes dynamic loading on
+       systems that prepend an underscore to symbols.  At the present
+       the only system that I know of that needs this is OpenBSD.
+       BUT YOU MUST RECOMPILE ALL OF YOUR OLD MODULES AGAINST THIS BUILD.
+       Added getchannels() method ( How did I manage to delay this
+       untill now ).  Some document changes but nothing substantial.
+       Also fixed the png read/write routines to handle all colorspaces
+       and images with alpha information.  Also now it's possible to 
+       have Imager guess the format of the files to load or save 
+       when passing files to read or save from the filename.
+       Also all of the tests except dynamic loading now pass on OS/2.
+
+0.32    Tue Feb 29 17:26:00 2000 CPAN RELEASE
+          - Added the getcolorcount method.  Fixed interlace handling
+       on png images.  Fixed the missing channel count in crop()
+       method. Rewrote most of t1lib database stuff - created color
+       and font classes.  T1 stuff is mostly done - TT things were
+       rewritten too and now include most of what is needed for
+       pixmap caching.  Added documentation for fonts.  Comments have
+       been added to some of the relevant c-routines.  Added a copy()
+       function in Imager.xs and a corresponding method name.
+       Changed the underlying data type for the raw XS images from
+       pointers to objects - this will hopefully catch the most
+       basic errors and keep the segfaulting down.  This means that
+       all of the underlying XS calls for readjpeg, readgif, readpng 
+       and readraw do not take the first parameter any more.  
+       Made fixes to keep it not spewing warning on 5.004 perl.        
+
+       **** If you had any code that didn't use the OO interface ****
+       ****         It will probably not work any longer         ****
+       
+0.33   Beta -- No final
+         - Fixed the end message from Imager 0.32.  Destroy called
+       on an empty image.  Did some work on the polygon method.  
+       Some clean up in the Makefile.PL script.  Fixed a buffer
+       overrun in the t_transform in Imager.XS.  Fixed the 
+       error handling in the jpeg loader.  It now correctly
+       returns undef if a load on an image fails.  It also 
+       sends the error messages to the log file.  Added errstr()
+       method to the image object.  Added a new way to read()
+       objects from scalars.  So far this is only implemented for
+       jpeg, png and gif.  ppm and raw soon - as always if someone
+       wants to do an overhaul on the ppm stuff feel free.  It seems
+       like such a basic format that interfacing with a library is more
+       work than implementing all of the needed routines instead.
+
+0.34    Beta -- No final
+         - Bunch of documentation fixes, backed out ppm code.
+       Put in TonyC's giant transform2 patch.  Fixed the patch
+       to make it ansi compliant.  Fixed a bunch of bugs in the
+       Freetype code with regard to vertical and horizontal 
+       positioning and bounding boxes.  Cleaned up a lot of the
+       code so it runs under -Wall.  Code that is still in 
+       development such as the polygon converter do not compile
+       cleanly.  Fixed the non antialiased versions of truetype
+       dump to image routines.  Also removed the FIXME for the 
+       hardcoding of antialias in the Imager string method.
+       Fixed sign error and a missing cache of the bounding box
+       calculation for the rasterize function.  Removed some
+       debugging code I forgot to remove.  Added iolayer.h
+       and iolayer.c but they don't do anything for now.
+       
+0.35   pre2 -- No time yet
+         - Fixed some compile warnings for various files under -Wall.
+       Added functionality for jpeg reading of seekable files, it's not
+       really working yet.  This version is pretty much *not* working.
+       Do not install unless you intend to do a lot of development.
+       Repeat - it doesn't even pass tests (but it compiles).  Ok now reading
+       jpegs works from scalars, my guess is that it also works from non
+       seeking sources such as sockets or pipes.
+
+0.35  pre3 - No time yet
+         - Added the *right* patch from Tony which combines
+       the common code from i_readgif and i_readgif_scalar into
+       i_readgif_low.  Added tiff reading support through iolayer.
+
+0.35  pre4 - No time yet
+         - Added tiff writing (no options) support through
+       iolayer.  Also made some small fixes for the iolayer reading
+       (was always doing two reads when one was needed).  Patched the
+       Imager::read() call so that it now uses a mixture of old and new
+       functions.  
+
+0.35  pre5 - No time yet
+         - Fixed various gnu'isms in the c code (some bugs in the link list
+       implmentation).  Fixed missing #skip codes when gif format is not
+       present in any form.  Added fixes for 5.004_04 in the transform2 function.
+       Made sure it compiles cleanly with sun's cc.  Switched from a .jpeg
+       for transform2 check to a .ppm file so it runs when jpeg is not 
+       present.  Added a test for tiff files in t10formats.t.
+
+
+0.35  pre6 - No time yet
+         - Fixes to Makefile.PL.  Should find freetype includes on more
+       distributions now.  Ran tests on Solaris and Hpux, minor fixes.
+       Compiles with some warnings on with both hpux and solaris' cc.  
+       Made some minor changes to the documentation.  Fixes to tiff.c log
+       code.
+
+0.35  pre7 - No time yet
+         - Fixes 64 bit bug on freebsd.  While libtiff mirrors the effects of
+       lseek it's toff_t is a uint32, while lseek uses off_t which can be a 64
+       bit quantity.  Added the IM_LFLAGS environment variable to help
+       people with broken libgifs (that want to link with X).
+
+0.35  Sun Jan 28 19:42:59 EST 2001
+         - More makefile fixes, fixed a few signedness warnings.  
+       Checked to see if it compiled cleanly on Solaris and HPUX.
+       Fixed a 5.004_04 warning and added more ENV flags for makefile.
+
+0.36   Mon Jan 29 09:36:11 EST 2001
+         - String as 0 or "" caused an error in $img->string().  Fixed a 
+       documentation error invoving string() method syntax.  Merged a patch
+       for non antialised truetype fonts.  Fixed an error in the Makefile.PL
+       which caused a makefile to be generated that bombed with sgi's make.
+
+0.37   Mon Tue 30 09:36:11 EST 2001
+         - Several documentation fixes.  Pod documentation for almost every
+       function in image.c.  Added sys/types.h include in iolayer which was
+       causing problems on various linux systems.
+
+0.38   pre1 - No time yet
+         - Fixed a braindamaged fix on the Makefile.PL file.  Moved the 
+       code for Imager::Color into lib/Imager/Color.pm.  Wrote some pod
+       about how it works.  Made the names of Imager::Color XS routines
+       all begin with ICL_ and used the prefix rules of XS to get nice names
+       in perl.  Found a bug (not fixed) in how XS handles 
+       returning an object to an object it had as a parameter (double
+       free).  
+
+0.38   pre2 - No time yet
+         - Fixes lots of for documentation, patch for freetype bounding
+       box handling.  Split put code for Imager::Font into Font.pm and added
+       more documentation for Font.pm.  Added string local ascender and
+       descender for tt fonts.  Note that this needs to be added to t1 fonts
+       before 0.38 final.
+
+0.38   pre3 - No time yet
+         - Fixed an in consistency in the bounding box functions for t1
+       fonts.  Now both versions get the 6 argument bounding_box method
+       described in Imager::Font.  Started converting the comments in 
+       font.c so that they are viewable by doco.perl.  Added two examples
+       of filters.  Need to make them more usefull and then add more
+       notes than are in compile.txt.
+
+
+0.38   pre4 - No time yet
+         - Completed adding pod comments to font.c, tiff.c and iolayer.c.
+       Those along with image.c should now have every single function 
+       described in pod format.
+
+0.38   pre5 - No time yet
+         - Replaced ppm.c with pnm.c which adds support for pbm/pgm/ppm
+       files ascii and binary formats.  Added patches for the gif routines.
+       Patched some of the color quantizing routines (Leolo and TonyC).
+       There is one bomb and one warning in this test, and frankly I don't
+       see why they are suddenly there.
+
+0.38   pre6 - No time yet
+         - Patch from Tony that fixes infix when Parse::RecDescent is present.
+       Checked some cases where malloc/free were used instead of mymalloc/myfree.
+       Added bufchain iolayer code.  You can now write to a chain of buffers and
+       later slurp it all into a single perl scalar.  Found some oddity of t/t10
+       test not giving the right return value when malloc debugging was enabled.
+       Fixed some of the logging code and the malloc debugging options.  Added 
+       more tests for tiffs.
+
+0.38   pre7 - No time yet
+         - Added i_gradgen code and put it into the filters hash.  Think a
+       seperate pod for filters would be a good idea.  Also removed some of the
+       debugging code from the iolayer.  Added pod comments to filters.c and
+       looked over the code.
+
+0.38   pre8 - No time yet
+         - limited Win32 support, Imager installs and builds using VC++,
+       but there's no image/font format support yet.
+
+0.38  pre9 - No time yet
+         - Added lots of color quantization code from Tony with benchmarks.
+       Also fixes ugly stack overrun in old version.  Added fixes for the lmfixed
+       problem.  Four of them, let's see which is fastest.  This version adds
+       some voronoi partitioning - it's dog slow but it's a reference implementation
+       to check if faster algorithms are doing the right thing [tm].  Added a check
+       for giflib 3.
+
+
+
+~~~~~~~~~~~~~^ ^ ^~~~~~~~~~~~~~
+
+
+0.40   TODO list
+       iolayer:
+       - Add scalar/mmap to iolayer
+       - Add close() code to iolayer
+       - Merge callback interface into iolayer
+       - Add interface for writing to all formats but tiff
+       - Add interface for reading for png (started), 
+         gif (merge with cb patch), ppm and raw
+       - Add make new tests once all formats support io_layer
+       - Implment the maxread threshold (Indicates how far 
+         a library can read before it indicates that it's done).
+
+       MultiImage & metadata support:
+       - Figure what interface should be between C and perl?
+       - How to store data in the C interface for tags/metadata?
+
+       Old sins:
+       - Make sure everything is doable with the OO interface
+       - Split the other classes into seperate files
+       - Compile with memory debugging enabled and fix leaks
+       - Check if hashbox code is choosing the wrong closest color
+
+       Documentation:
+       - Add to the documentation
+       - Write a tutorial?
+       - Write a guide to installing the helper libraries
+       - Go through the entire project and add comments in pod
+         so doco.perl can be used to read them.
+
+===================================================
+
+       For latest versions check the Imager-devel pages:
+       http://www.eecs.umich.edu/~addi/perl/Imager/devel/
+
+===================================================
+
diff --git a/Imager.pm b/Imager.pm
new file mode 100644 (file)
index 0000000..a2d14c6
--- /dev/null
+++ b/Imager.pm
@@ -0,0 +1,2270 @@
+package Imager;
+
+
+
+use strict;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS);
+use IO::File;
+
+use Imager::Color;
+use Imager::Font;
+
+@EXPORT_OK = qw(
+               init
+               init_log
+               DSO_open
+               DSO_close
+               DSO_funclist
+               DSO_call
+               
+               load_plugin
+               unload_plugin
+               
+               i_list_formats
+               i_has_format
+               
+               i_color_new
+               i_color_set
+               i_color_info
+               
+               i_img_empty
+               i_img_empty_ch
+               i_img_exorcise
+               i_img_destroy
+
+               i_img_info
+
+               i_img_setmask
+               i_img_getmask
+
+               i_draw
+               i_line_aa
+               i_box
+               i_box_filled
+               i_arc
+               
+               i_bezier_multi
+               i_poly_aa
+
+               i_copyto
+               i_rubthru
+               i_scaleaxis
+               i_scale_nn
+               i_haar
+               i_count_colors
+               
+               
+               i_gaussian
+               i_conv
+               
+               i_img_diff
+
+               i_init_fonts
+               i_t1_new
+               i_t1_destroy
+               i_t1_set_aa
+               i_t1_cp
+               i_t1_text
+               i_t1_bbox
+
+
+               i_tt_set_aa
+               i_tt_cp
+               i_tt_text
+               i_tt_bbox
+
+               i_readjpeg
+               i_writejpeg
+
+               i_readjpeg_wiol
+               i_writejpeg_wiol
+
+               i_readtiff_wiol
+               i_writetiff_wiol
+
+               i_readpng
+               i_writepng
+
+               i_readgif
+               i_readgif_callback
+               i_writegif
+               i_writegifmc
+               i_writegif_gen
+               i_writegif_callback
+
+               i_readpnm_wiol
+               i_writeppm
+
+               i_readraw
+               i_writeraw
+
+               i_contrast
+               i_hardinvert
+               i_noise
+               i_bumpmap
+               i_postlevels
+               i_mosaic
+               i_watermark
+               
+               malloc_state
+
+               list_formats
+               
+               i_gifquant
+
+               newfont
+               newcolor
+               newcolour
+               NC
+               NF
+               
+);
+
+
+
+@EXPORT=qw( 
+          init_log
+          i_list_formats
+          i_has_format
+          malloc_state
+          i_color_new
+
+          i_img_empty
+          i_img_empty_ch
+         );
+
+%EXPORT_TAGS=
+  (handy => [qw(
+               newfont
+               newcolor
+               NF
+               NC
+              )],
+   all => [@EXPORT_OK],
+   default => [qw(
+                 load_plugin
+                 unload_plugin
+                )]);
+
+
+BEGIN {
+  require Exporter;
+  require DynaLoader;
+
+  $VERSION = '0.38pre9';
+  @ISA = qw(Exporter DynaLoader);
+  bootstrap Imager $VERSION;
+}
+
+BEGIN {
+  i_init_fonts(); # Initialize font engines
+  for(i_list_formats()) { $formats{$_}++; }
+
+  if ($formats{'t1'}) {
+    i_t1_set_aa(1);
+  }
+
+  if (!$formats{'t1'} and !$formats{'tt'}) {
+    $fontstate='no font support';
+  }
+
+  %OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
+
+  $DEBUG=0;
+
+  $filters{contrast}={
+                     callseq => ['image','intensity'],
+                     callsub => sub { my %hsh=@_; i_contrast($hsh{image},$hsh{intensity}); } 
+                    };
+
+  $filters{noise} ={
+                   callseq => ['image', 'amount', 'subtype'],
+                   defaults => { amount=>3,subtype=>0 },
+                   callsub => sub { my %hsh=@_; i_noise($hsh{image},$hsh{amount},$hsh{subtype}); }
+                  };
+
+  $filters{hardinvert} ={
+                        callseq => ['image'],
+                        defaults => { },
+                        callsub => sub { my %hsh=@_; i_hardinvert($hsh{image}); }
+                       };
+
+  $filters{autolevels} ={
+                        callseq => ['image','lsat','usat','skew'],
+                        defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 },
+                        callsub => sub { my %hsh=@_; i_autolevels($hsh{image},$hsh{lsat},$hsh{usat},$hsh{skew}); }
+                       };
+
+  $filters{turbnoise} ={
+                       callseq => ['image'],
+                       defaults => { xo=>0.0,yo=>0.0,scale=>10.0 },
+                       callsub => sub { my %hsh=@_; i_turbnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{scale}); }
+                      };
+
+  $filters{radnoise} ={
+                      callseq => ['image'],
+                      defaults => { xo=>100,yo=>100,ascale=>17.0,rscale=>0.02 },
+                      callsub => sub { my %hsh=@_; i_radnoise($hsh{image},$hsh{xo},$hsh{yo},$hsh{rscale},$hsh{ascale}); }
+                     };
+
+  $filters{conv} ={
+                      callseq => ['image', 'coef'],
+                      defaults => { },
+                      callsub => sub { my %hsh=@_; i_conv($hsh{image},$hsh{coef}); }
+                     };
+
+  $filters{gradgen} ={
+                      callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
+                      defaults => { },
+                      callsub => sub { my %hsh=@_; i_gradgen($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
+                     };
+
+  $filters{nearest_color} ={
+                           callseq => ['image', 'xo', 'yo', 'colors', 'dist'],
+                           defaults => { },
+                           callsub => sub { my %hsh=@_; i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
+                          };
+
+  $FORMATGUESS=\&def_guess_type;
+}
+
+#
+# Non methods
+#
+
+# initlize Imager
+# NOTE: this might be moved to an import override later on
+
+#sub import {
+#  my $pack = shift;
+#  (look through @_ for special tags, process, and remove them);   
+#  use Data::Dumper;
+#  print Dumper($pack);
+#  print Dumper(@_);
+#}
+
+sub init {
+  my %parms=(loglevel=>1,@_);
+  if ($parms{'log'}) {
+    init_log($parms{'log'},$parms{'loglevel'});
+  }
+
+#    if ($parms{T1LIB_CONFIG}) { $ENV{T1LIB_CONFIG}=$parms{T1LIB_CONFIG}; }
+#    if ( $ENV{T1LIB_CONFIG} and ( $fontstate eq 'missing conf' )) {
+#      i_init_fonts();
+#      $fontstate='ok';
+#    }
+}
+
+END {
+  if ($DEBUG) {
+    print "shutdown code\n";
+    #  for(keys %instances) { $instances{$_}->DESTROY(); }
+    malloc_state(); # how do decide if this should be used? -- store something from the import
+    print "Imager exiting\n";
+  }
+}
+
+# Load a filter plugin 
+
+sub load_plugin {
+  my ($filename)=@_;
+  my $i;
+  my ($DSO_handle,$str)=DSO_open($filename);
+  if (!defined($DSO_handle)) { $Imager::ERRSTR="Couldn't load plugin '$filename'\n"; return undef; }
+  my %funcs=DSO_funclist($DSO_handle);
+  if ($DEBUG) { print "loading module $filename\n"; $i=0; for(keys %funcs) { printf("  %2d: %s\n",$i++,$_); } }
+  $i=0;
+  for(keys %funcs) { if ($filters{$_}) { $ERRSTR="filter '$_' already exists\n"; DSO_close($DSO_handle); return undef; } }
+
+  $DSOs{$filename}=[$DSO_handle,\%funcs];
+
+  for(keys %funcs) { 
+    my $evstr="\$filters{'".$_."'}={".$funcs{$_}.'};';
+    $DEBUG && print "eval string:\n",$evstr,"\n";
+    eval $evstr;
+    print $@ if $@;
+  }
+  return 1;
+}
+
+# Unload a plugin
+
+sub unload_plugin {
+  my ($filename)=@_;
+
+  if (!$DSOs{$filename}) { $ERRSTR="plugin '$filename' not loaded."; return undef; }
+  my ($DSO_handle,$funcref)=@{$DSOs{$filename}};
+  for(keys %{$funcref}) {
+    delete $filters{$_};
+    $DEBUG && print "unloading: $_\n";
+  }
+  my $rc=DSO_close($DSO_handle);
+  if (!defined($rc)) { $ERRSTR="unable to unload plugin '$filename'."; return undef; }
+  return 1;
+}
+
+
+#
+# Methods to be called on objects.
+#
+
+# Create a new Imager object takes very few parameters.
+# usually you call this method and then call open from
+# the resulting object
+
+sub new {
+  my $class = shift;
+  my $self ={};
+  my %hsh=@_;
+  bless $self,$class;
+  $self->{IMG}=undef;    # Just to indicate what exists
+  $self->{ERRSTR}=undef; #
+  $self->{DEBUG}=$DEBUG;
+  $self->{DEBUG} && print "Initialized Imager\n";
+  if ($hsh{xsize} && $hsh{ysize}) { $self->img_set(%hsh); }
+  return $self;
+}
+
+
+# Copy an entire image with no changes 
+# - if an image has magic the copy of it will not be magical
+
+sub copy {
+  my $self = shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  my $newcopy=Imager->new();
+  $newcopy->{IMG}=i_img_new();
+  i_copy($newcopy->{IMG},$self->{IMG});
+  return $newcopy;
+}
+
+# Paste a region
+
+sub paste {
+  my $self = shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  my %input=(left=>0, top=>0, @_);
+  unless($input{img}) {
+    $self->{ERRSTR}="no source image";
+    return;
+  }
+  $input{left}=0 if $input{left} <= 0;
+  $input{top}=0 if $input{top} <= 0;
+  my $src=$input{img};
+  my($r,$b)=i_img_info($src->{IMG});
+
+  i_copyto($self->{IMG}, $src->{IMG}, 
+          0,0, $r, $b, $input{left}, $input{top});
+  return $self;  # What should go here??
+}
+
+# Crop an image - i.e. return a new image that is smaller
+
+sub crop {
+  my $self=shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  my %hsh=(left=>0,right=>0,top=>0,bottom=>0,@_);
+
+  my ($w,$h,$l,$r,$b,$t)=($self->getwidth(),$self->getheight(),
+                               @hsh{qw(left right bottom top)});
+  $l=0 if not defined $l;
+  $t=0 if not defined $t;
+  $r=$self->getwidth if not defined $r;
+  $b=$self->getheight if not defined $b;
+
+  ($l,$r)=($r,$l) if $l>$r;
+  ($t,$b)=($b,$t) if $t>$b;
+
+  if ($hsh{'width'}) { 
+    $l=int(0.5+($w-$hsh{'width'})/2); 
+    $r=$l+$hsh{'width'}; 
+  } else {
+    $hsh{'width'}=$r-$l;
+  }
+  if ($hsh{'height'}) { 
+    $b=int(0.5+($h-$hsh{'height'})/2); 
+    $t=$h+$hsh{'height'}; 
+  } else {
+    $hsh{'height'}=$b-$t;
+  }
+
+#    print "l=$l, r=$r, h=$hsh{'width'}\n";
+#    print "t=$t, b=$b, w=$hsh{'height'}\n";
+
+  my $dst=Imager->new(xsize=>$hsh{'width'},ysize=>$hsh{'height'},channels=>$self->getchannels());
+
+  i_copyto($dst->{IMG},$self->{IMG},$l,$t,$r,$b,0,0);
+  return $dst;
+}
+
+# Sets an image to a certain size and channel number
+# if there was previously data in the image it is discarded
+
+sub img_set {
+  my $self=shift;
+
+  my %hsh=(xsize=>100,ysize=>100,channels=>3,@_);
+
+  if (defined($self->{IMG})) {
+    i_img_destroy($self->{IMG});
+    undef($self->{IMG});
+  }
+
+  $self->{IMG}=Imager::ImgRaw::new($hsh{'xsize'},$hsh{'ysize'},$hsh{'channels'});
+}
+
+# Read an image from file
+
+sub read {
+  my $self = shift;
+  my %input=@_;
+  my ($fh, $fd, $IO);
+
+  if (defined($self->{IMG})) {
+    i_img_destroy($self->{IMG});
+    undef($self->{IMG});
+  }
+
+  if (!$input{fd} and !$input{file} and !$input{data}) { $self->{ERRSTR}='no file, fd or data parameter'; return undef; }
+  if ($input{file}) {
+    $fh = new IO::File($input{file},"r");
+    if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
+    binmode($fh);
+    $fd = $fh->fileno();
+  }
+  if ($input{fd}) { $fd=$input{fd} };
+
+  # FIXME: Find the format here if not specified
+  # yes the code isn't here yet - next week maybe?
+
+  if (!$input{type} and $input{file}) { $input{type}=$FORMATGUESS->($input{file}); }
+  if (!$formats{$input{type}}) { $self->{ERRSTR}='format not supported'; return undef; }
+
+  my %iolready=(jpeg=>1, tiff=>1, pnm=>1);
+
+  if ($iolready{$input{type}}) {
+    # Setup data source
+    $IO = io_new_fd($fd); # sort of simple for now eh?
+
+    if ( $input{type} eq 'jpeg' ) {
+      ($self->{IMG},$self->{IPTCRAW})=i_readjpeg_wiol( $IO );
+      if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read jpeg image'; return undef; }
+      $self->{DEBUG} && print "loading a jpeg file\n";
+      return $self;
+    }
+
+    if ( $input{type} eq 'tiff' ) {
+      $self->{IMG}=i_readtiff_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+      if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read tiff image'; return undef; }
+      $self->{DEBUG} && print "loading a tiff file\n";
+      return $self;
+    }
+
+    if ( $input{type} eq 'pnm' ) {
+      $self->{IMG}=i_readpnm_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+      if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read pnm image'; return undef; }
+      $self->{DEBUG} && print "loading a pnm file\n";
+      return $self;
+    }
+
+  } else {
+
+  # Old code for reference while changing the new stuff
+
+
+  if (!$input{type} and $input{file}) { $input{type}=$FORMATGUESS->($input{file}); }
+  if (!$input{type}) { $self->{ERRSTR}='type parameter missing and not possible to guess from extension'; return undef; }
+
+  if (!$formats{$input{type}}) { $self->{ERRSTR}='format not supported'; return undef; }
+
+  if ($input{file}) {
+    $fh = new IO::File($input{file},"r");
+    if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
+    binmode($fh);
+    $fd = $fh->fileno();
+  }
+  if ($input{fd}) { $fd=$input{fd} };
+
+  if ( $input{type} eq 'gif' ) {
+    if (exists $input{data}) { $self->{IMG}=i_readgif_scalar($input{data}); }
+    else { $self->{IMG}=i_readgif( $fd ) }
+    if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read gif image'; return undef; }
+    $self->{DEBUG} && print "loading a gif file\n";
+  } elsif ( $input{type} eq 'jpeg' ) {
+    if (exists $input{data}) { ($self->{IMG},$self->{IPTCRAW})=i_readjpeg_scalar($input{data}); }
+    else { ($self->{IMG},$self->{IPTCRAW})=i_readjpeg( $fd ); }
+    if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read jpeg image'; return undef; }
+    $self->{DEBUG} && print "loading a jpeg file\n";
+  } elsif ( $input{type} eq 'png' ) {
+    if (exists $input{data}) { $self->{IMG}=i_readpng_scalar($input{data}); }
+    else { $self->{IMG}=i_readpng( $fd ); }
+    if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read png image'; return undef; }
+    $self->{DEBUG} && print "loading a png file\n";
+  } elsif ( $input{type} eq 'raw' ) {
+    my %params=(datachannels=>3,storechannels=>3,interleave=>1);
+    for(keys(%input)) { $params{$_}=$input{$_}; }
+
+    if ( !($params{xsize} && $params{ysize}) ) { $self->{ERRSTR}='missing xsize or ysize parameter for raw'; return undef; }
+    $self->{IMG}=i_readraw( $fd, $params{xsize}, $params{ysize},
+                          $params{datachannels}, $params{storechannels}, $params{interleave});
+    if ( !defined($self->{IMG}) ) { $self->{ERRSTR}='unable to read raw image'; return undef; }
+    $self->{DEBUG} && print "loading a raw file\n";
+  }
+  return $self;
+  }
+}
+
+
+# Write an image to file
+
+sub write {
+  my $self = shift;
+  my %input=(jpegquality=>75, gifquant=>'mc', lmdither=>6.0, lmfixed=>[], @_);
+  my ($fh, $rc, $fd, $IO);
+
+  my %iolready=( tiff=>1 ); # this will be SO MUCH BETTER once they are all in there
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if (!$input{file} and !$input{'fd'} and !$input{'data'}) { $self->{ERRSTR}='file/fd/data parameter missing'; return undef; }
+  if (!$input{type}) { $input{type}=$FORMATGUESS->($input{file}); }
+  if (!$input{type}) { $self->{ERRSTR}='type parameter missing and not possible to guess from extension'; return undef; }
+
+  if (!$formats{$input{type}}) { $self->{ERRSTR}='format not supported'; return undef; }
+
+  if (exists $input{'fd'}) {
+    $fd=$input{'fd'};
+  } elsif (exists $input{'data'}) {
+    $IO = Imager::io_new_bufchain();
+  } else {
+    $fh = new IO::File($input{file},"w+");
+    if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
+    binmode($fh);
+    $fd = $fh->fileno();
+  }
+
+
+
+  if ($iolready{$input{type}}) {
+    if ($fd) {
+      $IO = io_new_fd($fd);
+    }
+
+    if ($input{type} eq 'tiff') {
+      if (!i_writetiff_wiol($self->{IMG}, $IO)) { $self->{ERRSTR}='Could not write to buffer'; return undef; }
+    }
+
+    my $data = io_slurp($IO);
+    if (!$data) { $self->{ERRSTR}='Could not slurp from buffer'; return undef; }
+
+    ${$input{data}} = $data;
+    return $self;
+  } else {
+
+    if ( $input{type} eq 'gif' ) {
+      if (not $input{gifplanes}) {
+       my $gp;
+       my $count=i_count_colors($self->{IMG}, 256);
+       $gp=8 if $count == -1;
+       $gp=1 if not $gp and $count <= 2;
+       $gp=2 if not $gp and $count <= 4;
+       $gp=3 if not $gp and $count <= 8;
+       $gp=4 if not $gp and $count <= 16;
+       $gp=5 if not $gp and $count <= 32;
+       $gp=6 if not $gp and $count <= 64;
+       $gp=7 if not $gp and $count <= 128;
+       $input{gifplanes} = $gp || 8;
+      }
+
+      if ($input{gifplanes}>8) {
+       $input{gifplanes}=8;
+      }
+      if ($input{gifquant} eq 'gen' || $input{callback}) {
+
+
+       if ($input{gifquant} eq 'lm') {
+
+         $input{make_colors} = 'addi';
+         $input{translate} = 'perturb';
+         $input{perturb} = $input{lmdither};
+       } elsif ($input{gifquant} eq 'gen') {
+         # just pass options through
+       } else {
+         $input{make_colors} = 'webmap'; # ignored
+         $input{translate} = 'giflib';
+       }
+
+       if ($input{callback}) {
+         defined $input{maxbuffer} or $input{maxbuffer} = -1;
+         $rc = i_writegif_callback($input{callback}, $input{maxbuffer},
+                                   \%input, $self->{IMG});
+       } else {
+         $rc = i_writegif_gen($fd, \%input, $self->{IMG});
+       }
+
+
+
+      } elsif ($input{gifquant} eq 'lm') {
+       $rc=i_writegif($self->{IMG},$fd,$input{gifplanes},$input{lmdither},$input{lmfixed});
+      } else {
+       $rc=i_writegifmc($self->{IMG},$fd,$input{gifplanes});
+      }
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write gif image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a gif file\n";
+
+    } elsif ( $input{type} eq 'jpeg' ) {
+      $rc=i_writejpeg($self->{IMG},$fd,$input{jpegquality});
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write jpeg image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a jpeg file\n";
+    } elsif ( $input{type} eq 'png' ) { 
+      $rc=i_writepng($self->{IMG},$fd);
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write png image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a png file\n";
+    } elsif ( $input{type} eq 'pnm' ) {
+      $rc=i_writeppm($self->{IMG},$fd);
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write pnm image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a pnm file\n";
+    } elsif ( $input{type} eq 'raw' ) {
+      $rc=i_writeraw($self->{IMG},$fd);
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write raw image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a raw file\n";
+    } elsif ( $input{type} eq 'tiff' ) {
+      $rc=i_writetiff_wiol($self->{IMG},io_new_fd($fd) );
+      if ( !defined($rc) ) {
+       $self->{ERRSTR}='unable to write tiff image'; return undef;
+      }
+      $self->{DEBUG} && print "writing a tiff file\n";
+    }
+
+  }
+  return $self;
+}
+
+sub write_multi {
+  my ($class, $opts, @images) = @_;
+
+  if ($opts->{type} eq 'gif') {
+    # translate to ImgRaw
+    if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
+      $ERRSTR = "Usage: Imager->write_multi({ options }, @images)";
+      return 0;
+    }
+    my @work = map $_->{IMG}, @images;
+    if ($opts->{callback}) {
+      # Note: you may need to fix giflib for this one to work
+      my $maxbuffer = $opts->{maxbuffer};
+      defined $maxbuffer or $maxbuffer = -1; # max by default
+      return i_writegif_callback($opts->{callback}, $maxbuffer,
+                                $opts, @work);
+    }
+    if ($opts->{fd}) {
+      return i_writegif_gen($opts->{fd}, $opts, @work);
+    }
+    else {
+      my $fh = IO::File->new($opts->{file}, "w+");
+      unless ($fh) {
+       $ERRSTR = "Error creating $opts->{file}: $!";
+       return 0;
+      }
+      binmode($fh);
+      return i_writegif_gen(fileno($fh), $opts, @work);
+    }
+  }
+  else {
+    $ERRSTR = "Sorry, write_multi doesn't support $opts->{type} yet";
+    return 0;
+  }
+}
+
+# Destroy an Imager object
+
+sub DESTROY {
+  my $self=shift;
+  #    delete $instances{$self};
+  if (defined($self->{IMG})) {
+    i_img_destroy($self->{IMG});
+    undef($self->{IMG});
+  } else {
+#    print "Destroy Called on an empty image!\n"; # why did I put this here??
+  }
+}
+
+# Perform an inplace filter of an image
+# that is the image will be overwritten with the data
+
+sub filter {
+  my $self=shift;
+  my %input=@_;
+  my %hsh;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if (!$input{type}) { $self->{ERRSTR}='type parameter missing'; return undef; }
+
+  if ( (grep { $_ eq $input{type} } keys %filters) != 1) {
+    $self->{ERRSTR}='type parameter not matching any filter'; return undef;
+  }
+
+  if (defined($filters{$input{type}}{defaults})) {
+    %hsh=('image',$self->{IMG},%{$filters{$input{type}}{defaults}},%input);
+  } else {
+    %hsh=('image',$self->{IMG},%input);
+  }
+
+  my @cs=@{$filters{$input{type}}{callseq}};
+
+  for(@cs) {
+    if (!defined($hsh{$_})) {
+      $self->{ERRSTR}="missing parameter '$_' for filter ".$input{type}; return undef;
+    }
+  }
+
+  &{$filters{$input{type}}{callsub}}(%hsh);
+
+  my @b=keys %hsh;
+
+  $self->{DEBUG} && print "callseq is: @cs\n";
+  $self->{DEBUG} && print "matching callseq is: @b\n";
+
+  return $self;
+}
+
+# Scale an image to requested size and return the scaled version
+
+sub scale {
+  my $self=shift;
+  my %opts=(scalefactor=>0.5,type=>'max',qtype=>'normal',@_);
+  my $img = Imager->new();
+  my $tmp = Imager->new();
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if ($opts{xpixels} and $opts{ypixels} and $opts{type}) {
+    my ($xpix,$ypix)=( $opts{xpixels}/$self->getwidth() , $opts{ypixels}/$self->getheight() );
+    if ($opts{type} eq 'min') { $opts{scalefactor}=min($xpix,$ypix); }
+    if ($opts{type} eq 'max') { $opts{scalefactor}=max($xpix,$ypix); }
+  } elsif ($opts{xpixels}) { $opts{scalefactor}=$opts{xpixels}/$self->getwidth(); }
+  elsif ($opts{ypixels}) { $opts{scalefactor}=$opts{ypixels}/$self->getheight(); }
+
+  if ($opts{qtype} eq 'normal') {
+    $tmp->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
+    if ( !defined($tmp->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+    $img->{IMG}=i_scaleaxis($tmp->{IMG},$opts{scalefactor},1);
+    if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+    return $img;
+  }
+  if ($opts{'qtype'} eq 'preview') {
+    $img->{IMG}=i_scale_nn($self->{IMG},$opts{'scalefactor'},$opts{'scalefactor'}); 
+    if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+    return $img;
+  }
+  $self->{ERRSTR}='scale: invalid value for qtype'; return undef;
+}
+
+# Scales only along the X axis
+
+sub scaleX {
+  my $self=shift;
+  my %opts=(scalefactor=>0.5,@_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  my $img = Imager->new();
+
+  if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getwidth(); }
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},0);
+
+  if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+  return $img;
+}
+
+# Scales only along the Y axis
+
+sub scaleY {
+  my $self=shift;
+  my %opts=(scalefactor=>0.5,@_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  my $img = Imager->new();
+
+  if ($opts{pixels}) { $opts{scalefactor}=$opts{pixels}/$self->getheight(); }
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $img->{IMG}=i_scaleaxis($self->{IMG},$opts{scalefactor},1);
+
+  if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='unable to scale image'; return undef; }
+  return $img;
+}
+
+
+# Transform returns a spatial transformation of the input image
+# this moves pixels to a new location in the returned image.
+# NOTE - should make a utility function to check transforms for
+# stack overruns
+
+sub transform {
+  my $self=shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  my %opts=@_;
+  my (@op,@ropx,@ropy,$iop,$or,@parm,$expr,@xt,@yt,@pt,$numre);
+
+#  print Dumper(\%opts);
+#  xopcopdes
+
+  if ( $opts{'xexpr'} and $opts{'yexpr'} ) {
+    if (!$I2P) {
+      eval ("use Affix::Infix2Postfix;");
+      print $@;
+      if ( $@ ) {
+       $self->{ERRSTR}='transform: expr given and Affix::Infix2Postfix is not avaliable.'; 
+       return undef;
+      }
+      $I2P=Affix::Infix2Postfix->new('ops'=>[{op=>'+',trans=>'Add'},
+                                            {op=>'-',trans=>'Sub'},
+                                            {op=>'*',trans=>'Mult'},
+                                            {op=>'/',trans=>'Div'},
+                                            {op=>'-',type=>'unary',trans=>'u-'},
+                                            {op=>'**'},
+                                            {op=>'func',type=>'unary'}],
+                                    'grouping'=>[qw( \( \) )],
+                                    'func'=>[qw( sin cos )],
+                                    'vars'=>[qw( x y )]
+                                   );
+    }
+
+    @xt=$I2P->translate($opts{'xexpr'});
+    @yt=$I2P->translate($opts{'yexpr'});
+
+    $numre=$I2P->{'numre'};
+    @pt=(0,0);
+
+    for(@xt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'xopcodes'}},'Parm',$#pt); } else { push(@{$opts{'xopcodes'}},$_); } }
+    for(@yt) { if (/$numre/) { push(@pt,$_); push(@{$opts{'yopcodes'}},'Parm',$#pt); } else { push(@{$opts{'yopcodes'}},$_); } }
+    @{$opts{'parm'}}=@pt;
+  }
+
+#  print Dumper(\%opts);
+
+  if ( !exists $opts{'xopcodes'} or @{$opts{'xopcodes'}}==0) {
+    $self->{ERRSTR}='transform: no xopcodes given.';
+    return undef;
+  }
+
+  @op=@{$opts{'xopcodes'}};
+  for $iop (@op) { 
+    if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
+      $self->{ERRSTR}="transform: illegal opcode '$_'.";
+      return undef;
+    }
+    push(@ropx,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
+  }
+
+
+# yopcopdes
+
+  if ( !exists $opts{'yopcodes'} or @{$opts{'yopcodes'}}==0) {
+    $self->{ERRSTR}='transform: no yopcodes given.';
+    return undef;
+  }
+
+  @op=@{$opts{'yopcodes'}};
+  for $iop (@op) { 
+    if (!defined ($OPCODES{$iop}) and ($iop !~ /^\d+$/) ) {
+      $self->{ERRSTR}="transform: illegal opcode '$_'.";
+      return undef;
+    }
+    push(@ropy,(exists $OPCODES{$iop}) ? @{$OPCODES{$iop}} : $iop );
+  }
+
+#parameters
+
+  if ( !exists $opts{'parm'}) {
+    $self->{ERRSTR}='transform: no parameter arg given.';
+    return undef;
+  }
+
+#  print Dumper(\@ropx);
+#  print Dumper(\@ropy);
+#  print Dumper(\@ropy);
+
+  my $img = Imager->new();
+  $img->{IMG}=i_transform($self->{IMG},\@ropx,\@ropy,$opts{'parm'});
+  if ( !defined($img->{IMG}) ) { $self->{ERRSTR}='transform: failed'; return undef; }
+  return $img;
+}
+
+
+{
+  my $got_expr;
+  sub transform2 {
+    my ($opts, @imgs) = @_;
+
+    if (!$got_expr) {
+      # this is fairly big, delay loading it
+      eval "use Imager::Expr";
+      die $@ if $@;
+      ++$got_expr;
+    }
+
+    $opts->{variables} = [ qw(x y) ];
+    my ($width, $height) = @{$opts}{qw(width height)};
+    if (@imgs) {
+       $width ||= $imgs[0]->getwidth();
+       $height ||= $imgs[0]->getheight();
+       my $img_num = 1;
+       for my $img (@imgs) {
+           $opts->{constants}{"w$img_num"} = $img->getwidth();
+           $opts->{constants}{"h$img_num"} = $img->getheight();
+           $opts->{constants}{"cx$img_num"} = $img->getwidth()/2;
+           $opts->{constants}{"cy$img_num"} = $img->getheight()/2;
+           ++$img_num;
+       }
+    }
+    if ($width) {
+      $opts->{constants}{w} = $width;
+      $opts->{constants}{cx} = $width/2;
+    }
+    else {
+      $Imager::ERRSTR = "No width supplied";
+      return;
+    }
+    if ($height) {
+      $opts->{constants}{h} = $height;
+      $opts->{constants}{cy} = $height/2;
+    }
+    else {
+      $Imager::ERRSTR = "No height supplied";
+      return;
+    }
+    my $code = Imager::Expr->new($opts);
+    if (!$code) {
+      $Imager::ERRSTR = Imager::Expr::error();
+      return;
+    }
+
+    my $img = Imager->new();
+    $img->{IMG} = i_transform2($opts->{width}, $opts->{height}, $code->code(),
+                              $code->nregs(), $code->cregs(),
+                              [ map { $_->{IMG} } @imgs ]);
+    if (!defined $img->{IMG}) {
+      $Imager::ERRSTR = "transform2 failed";
+      return;
+    }
+
+    return $img;
+  }
+}
+
+
+
+
+
+
+
+
+sub rubthrough {
+  my $self=shift;
+  my %opts=(tx=>0,ty=>0,@_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  unless ($opts{src} && $opts{src}->{IMG}) { $self->{ERRSTR}='empty input image for source'; return undef; }
+
+  i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx},$opts{ty});
+  return $self;
+}
+
+
+
+# These two are supported for legacy code only
+
+sub i_color_new {
+  return Imager::Color->new($_[0], $_[1], $_[2], $_[3]);
+}
+
+sub i_color_set {
+  return Imager::Color::set($_[0], $_[1], $_[2], $_[3], $_[4]);
+}
+
+
+
+# Draws a box between the specified corner points.
+
+sub box {
+  my $self=shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  my $dflcl=i_color_new(255,255,255,255);
+  my %opts=(color=>$dflcl,xmin=>0,ymin=>0,xmax=>$self->getwidth()-1,ymax=>$self->getheight()-1,@_);
+
+  if (exists $opts{'box'}) { 
+    $opts{'xmin'} = min($opts{'box'}->[0],$opts{'box'}->[2]);
+    $opts{'xmax'} = max($opts{'box'}->[0],$opts{'box'}->[2]);
+    $opts{'ymin'} = min($opts{'box'}->[1],$opts{'box'}->[3]);
+    $opts{'ymax'} = max($opts{'box'}->[1],$opts{'box'}->[3]);
+  }
+
+  if ($opts{filled}) { i_box_filled($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},$opts{color}); }
+  else { i_box($self->{IMG},$opts{xmin},$opts{ymin},$opts{xmax},$opts{ymax},$opts{color}); }
+  return $self;
+}
+
+# Draws an arc - this routine SUCKS and is buggy - it sometimes doesn't work when the arc is a convex polygon
+
+sub arc {
+  my $self=shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  my $dflcl=i_color_new(255,255,255,255);
+  my %opts=(color=>$dflcl,
+           'r'=>min($self->getwidth(),$self->getheight())/3,
+           'x'=>$self->getwidth()/2,
+           'y'=>$self->getheight()/2,
+           'd1'=>0, 'd2'=>361, @_);
+  i_arc($self->{IMG},$opts{'x'},$opts{'y'},$opts{'r'},$opts{'d1'},$opts{'d2'},$opts{'color'}); 
+  return $self;
+}
+
+# Draws a line from one point to (but not including) the destination point
+
+sub line {
+  my $self=shift;
+  my $dflcl=i_color_new(0,0,0,0);
+  my %opts=(color=>$dflcl,@_);
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  unless (exists $opts{x1} and exists $opts{y1}) { $self->{ERRSTR}='missing begining coord'; return undef; }
+  unless (exists $opts{x2} and exists $opts{y2}) { $self->{ERRSTR}='missing ending coord'; return undef; }
+
+  if ($opts{antialias}) {
+    i_line_aa($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, $opts{color});
+  } else {
+    i_draw($self->{IMG},$opts{x1}, $opts{y1}, $opts{x2}, $opts{y2}, $opts{color});
+  }
+  return $self;
+}
+
+# Draws a line between an ordered set of points - It more or less just transforms this
+# into a list of lines.
+
+sub polyline {
+  my $self=shift;
+  my ($pt,$ls,@points);
+  my $dflcl=i_color_new(0,0,0,0);
+  my %opts=(color=>$dflcl,@_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if (exists($opts{points})) { @points=@{$opts{points}}; }
+  if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) {
+    @points=map { [ $opts{'x'}->[$_],$opts{'y'}->[$_] ] } (0..(scalar @{$opts{'x'}}-1));
+    }
+
+#  print Dumper(\@points);
+
+  if ($opts{antialias}) {
+    for $pt(@points) {
+      if (defined($ls)) { i_line_aa($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$opts{color}); }
+      $ls=$pt;
+    }
+  } else {
+    for $pt(@points) {
+      if (defined($ls)) { i_draw($self->{IMG},$ls->[0],$ls->[1],$pt->[0],$pt->[1],$opts{color}); }
+      $ls=$pt;
+    }
+  }
+  return $self;
+}
+
+# this the multipoint bezier curve 
+# this is here more for testing that actual usage since
+# this is not a good algorithm.  Usually the curve would be
+# broken into smaller segments and each done individually.
+
+sub polybezier {
+  my $self=shift;
+  my ($pt,$ls,@points);
+  my $dflcl=i_color_new(0,0,0,0);
+  my %opts=(color=>$dflcl,@_);
+
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  if (exists $opts{points}) {
+    $opts{'x'}=map { $_->[0]; } @{$opts{'points'}};
+    $opts{'y'}=map { $_->[1]; } @{$opts{'points'}};
+  }
+
+  unless ( @{$opts{'x'}} and @{$opts{'x'}} == @{$opts{'y'}} ) {
+    $self->{ERRSTR}='Missing or invalid points.';
+    return;
+  }
+
+  i_bezier_multi($self->{IMG},$opts{'x'},$opts{'y'},$opts{'color'});
+  return $self;
+}
+
+
+# destructive border - image is shrunk by one pixel all around
+
+sub border {
+  my ($self,%opts)=@_;
+  my($tx,$ty)=($self->getwidth()-1,$self->getheight()-1);
+  $self->polyline('x'=>[0,$tx,$tx,0,0],'y'=>[0,0,$ty,$ty,0],%opts);
+}
+
+
+# Get the width of an image
+
+sub getwidth {
+  my $self = shift;
+  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  return (i_img_info($self->{IMG}))[0];
+}
+
+# Get the height of an image
+
+sub getheight {
+  my $self = shift;
+  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  return (i_img_info($self->{IMG}))[1];
+}
+
+# Get number of channels in an image
+
+sub getchannels {
+  my $self = shift;
+  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  return i_img_getchannels($self->{IMG});
+}
+
+# Get channel mask
+
+sub getmask {
+  my $self = shift;
+  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  return i_img_getmask($self->{IMG});
+}
+
+# Set channel mask
+
+sub setmask {
+  my $self = shift;
+  my %opts = @_;
+  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+  i_img_setmask( $self->{IMG} , $opts{mask} );
+}
+
+# Get number of colors in an image
+
+sub getcolorcount {
+  my $self=shift;
+  my %opts=(maxcolors=>2**30,@_);
+  if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
+  my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
+  return ($rc==-1? undef : $rc);
+}
+
+# draw string to an image
+
+sub string {
+  my $self = shift;
+  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  my %input=('x'=>0, 'y'=>0, @_);
+  $input{string}||=$input{text};
+
+  unless(exists $input{string}) {
+    $self->{ERRSTR}="missing required parameter 'string'";
+    return;
+  }
+
+  unless($input{font}) {
+    $self->{ERRSTR}="missing required parameter 'font'";
+    return;
+  }
+
+  my $aa=1;
+  my $font=$input{'font'};
+  my $align=$font->{'align'} unless exists $input{'align'};
+  my $color=$input{'color'} || $font->{'color'};
+  my $size=$input{'size'}   || $font->{'size'};
+
+  if (!defined($size)) { $self->{ERRSTR}='No size parameter and no default in font'; return undef; }
+
+  $aa=$font->{'aa'} if exists $font->{'aa'};
+  $aa=$input{'aa'} if exists $input{'aa'};
+
+
+
+#  unless($font->can('text')) {
+#    $self->{ERRSTR}="font is unable to do what we need";
+#    return;
+#  }
+
+#  use Data::Dumper; 
+#  warn Dumper($font);
+
+#  print "Channel=".$input{'channel'}."\n";
+
+  if ( $font->{'type'} eq 't1' ) {
+    if ( exists $input{'channel'} ) {
+      Imager::Font::t1_set_aa_level($aa);
+      i_t1_cp($self->{IMG},$input{'x'},$input{'y'},
+             $input{'channel'},$font->{'id'},$size,
+             $input{'string'},length($input{'string'}),1);
+    } else {
+      Imager::Font::t1_set_aa_level($aa);
+      i_t1_text($self->{IMG},$input{'x'},$input{'y'},
+               $color,$font->{'id'},$size,
+               $input{'string'},length($input{'string'}),1);
+    }
+  }
+
+  if ( $font->{'type'} eq 'tt' ) {
+    if ( exists $input{'channel'} ) {
+      i_tt_cp($font->{'id'},$self->{IMG},$input{'x'},$input{'y'},$input{'channel'},
+             $size,$input{'string'},length($input{'string'}),$aa); 
+    } else {
+      i_tt_text($font->{'id'},$self->{IMG},$input{'x'},$input{'y'},$color,$size,
+               $input{'string'},length($input{'string'}),$aa); 
+    }
+  }
+
+  return $self;
+}
+
+
+
+
+
+# Shortcuts that can be exported
+
+sub newcolor { Imager::Color->new(@_); }
+sub newfont  { Imager::Font->new(@_); }
+
+*NC=*newcolour=*newcolor;
+*NF=*newfont;
+
+*open=\&read;
+*circle=\&arc;
+
+
+#### Utility routines
+
+sub errstr { $_[0]->{ERRSTR} }
+
+
+
+
+
+
+# Default guess for the type of an image from extension
+
+sub def_guess_type {
+  my $name=lc(shift);
+  my $ext;
+  $ext=($name =~ m/\.([^\.]+)$/)[0];
+  return 'tiff' if ($ext =~ m/^tiff?$/);
+  return 'jpeg' if ($ext =~ m/^jpe?g$/);
+  return 'pnm'  if ($ext =~ m/^p[pgb]m$/);
+  return 'png'  if ($ext eq "png");
+  return 'gif'  if ($ext eq "gif");
+  return ();
+}
+
+# get the minimum of a list
+
+sub min {
+  my $mx=shift;
+  for(@_) { if ($_<$mx) { $mx=$_; }}
+  return $mx;
+}
+
+# get the maximum of a list
+
+sub max {
+  my $mx=shift;
+  for(@_) { if ($_>$mx) { $mx=$_; }}
+  return $mx;
+}
+
+# string stuff for iptc headers
+
+sub clean {
+  my($str)=$_[0];
+  $str = substr($str,3);
+  $str =~ s/[\n\r]//g;
+  $str =~ s/\s+/ /g;
+  $str =~ s/^\s//;
+  $str =~ s/\s$//;
+  return $str;
+}
+
+# A little hack to parse iptc headers.
+
+sub parseiptc {
+  my $self=shift;
+  my(@sar,$item,@ar);
+  my($caption,$photogr,$headln,$credit);
+
+  my $str=$self->{IPTCRAW};
+
+  #print $str;
+
+  @ar=split(/8BIM/,$str);
+
+  my $i=0;
+  foreach (@ar) {
+    if (/^\004\004/) {
+      @sar=split(/\034\002/);
+      foreach $item (@sar) {
+       if ($item =~ m/^x/) { 
+         $caption=&clean($item);
+         $i++;
+       }
+       if ($item =~ m/^P/) { 
+         $photogr=&clean($item);
+         $i++;
+       }
+       if ($item =~ m/^i/) { 
+         $headln=&clean($item);
+         $i++;
+       }
+       if ($item =~ m/^n/) { 
+         $credit=&clean($item);
+         $i++;
+       }
+      }
+    }
+  }
+  return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
+}
+
+
+
+
+
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+# Below is the stub of documentation for your module. You better edit it!
+
+=head1 NAME
+
+Imager - Perl extension for Generating 24 bit Images
+
+=head1 SYNOPSIS
+
+  use Imager qw(init);
+
+  init();
+  $img = Imager->new();
+  $img->open(file=>'image.ppm',type=>'pnm')
+    || print "failed: ",$img->{ERRSTR},"\n";
+  $scaled=$img->scale(xpixels=>400,ypixels=>400);
+  $scaled->write(file=>'sc_image.ppm',type=>'pnm')
+    || print "failed: ",$scaled->{ERRSTR},"\n";
+
+=head1 DESCRIPTION
+
+Imager is a module for creating and altering images - It is not meant
+as a replacement or a competitor to ImageMagick or GD. Both are
+excellent packages and well supported.
+
+=head2 API
+
+Almost all functions take the parameters in the hash fashion.
+Example:
+
+  $img->open(file=>'lena.png',type=>'png');
+
+or just:
+
+  $img->open(file=>'lena.png');
+
+=head2 Basic concept
+
+An Image object is created with C<$img = Imager-E<gt>new()> Should
+this fail for some reason an explanation can be found in
+C<$Imager::ERRSTR> usually error messages are stored in
+C<$img-E<gt>{ERRSTR}>, but since no object is created this is the only
+way to give back errors.  C<$Imager::ERRSTR> is also used to report
+all errors not directly associated with an image object. Examples:
+
+  $img=Imager->new(); # This is an empty image (size is 0 by 0)
+  $img->open(file=>'lena.png',type=>'png'); # initializes from file
+
+or if you want to create an empty image:
+
+  $img=Imager->new(xsize=>400,ysize=>300,channels=>4);
+
+This example creates a completely black image of width 400 and
+height 300 and 4 channels.
+
+If you have an existing image, use img_set() to change it's dimensions
+- this will destroy any existing image data:
+
+  $img->img_set(xsize=>500, ysize=>500, channels=>4);
+
+Color objects are created by calling the Imager::Color->new()
+method:
+
+  $color = Imager::Color->new($red, $green, $blue);
+  $color = Imager::Color->new($red, $green, $blue, $alpha);
+  $color = Imager::Color->new("#C0C0FF"); # html color specification
+
+This object can then be passed to functions that require a color parameter.
+
+Coordinates in Imager have the origin in the upper left corner.  The
+horizontal coordinate increases to the right and the vertical
+downwards.
+
+=head2 Reading and writing images
+
+C<$img-E<gt>read()> generally takes two parameters, 'file' and 'type'.
+If the type of the file can be determined from the suffix of the file
+it can be omitted.  Format dependant parameters are: For images of
+type 'raw' two extra parameters are needed 'xsize' and 'ysize', if the
+'channel' parameter is omitted for type 'raw' it is assumed to be 3.
+gif and png images might have a palette are converted to truecolor bit
+when read.  Alpha channel is preserved for png images irregardless of
+them being in RGB or gray colorspace.  Similarly grayscale jpegs are
+one channel images after reading them.  For jpeg images the iptc
+header information (stored in the APP13 header) is avaliable to some
+degree. You can get the raw header with C<$img-E<gt>{IPTCRAW}>, but
+you can also retrieve the most basic information with
+C<%hsh=$img-E<gt>parseiptc()> as always patches are welcome.  Neither
+pnm nor tiff have extra options. Examples:
+
+  $img = Imager->new();
+  $img->read(file=>"cover.jpg") or die $img->errstr; # gets type from name
+
+  $img = Imager->new();
+  { local(*FH,$/); open(FH,"file.gif") or die $!; $a=<FH>; }
+  $img->read(data=>$a,type=>'gif') or die $img->errstr;
+
+The second example shows how to read an image from a scalar, this is
+usefull if your data originates from somewhere else than a filesystem
+such as a database over a DBI connection.
+
+*Note that load() is now an alias for read but will be removed later*
+
+C<$img-E<gt>write> has the same interface as C<read()>.  The earlier
+comments on C<read()> for autodetecting filetypes apply.  For jpegs
+quality can be adjusted via the 'jpegquality' parameter (0-100).  The
+number of colorplanes in gifs are set with 'gifplanes' and should be
+between 1 (2 color) and 8 (256 colors).  It is also possible to choose
+between two quantizing methods with the parameter 'gifquant'. If set
+to mc it uses the mediancut algorithm from either giflibrary. If set
+to lm it uses a local means algorithm. It is then possible to give
+some extra settings. lmdither is the dither deviation amount in pixels
+(manhattan distance).  lmfixed can be an array ref who holds an array
+of Imager::Color objects.  Note that the local means algorithm needs
+much more cpu time but also gives considerable better results than the
+median cut algorithm.
+
+Currently just for gif files, you can specify various options for the
+conversion from Imager's internal RGB format to the target's indexed
+file format.  If you set the gifquant option to 'gen', you can use the
+options specified under L<Quantization options>.
+
+To see what Imager is compiled to support the following code snippet
+is sufficient:
+
+  use Imager;
+  print "@{[keys %Imager::formats]}";
+
+=head2 Multi-image files
+
+Currently just for gif files, you can create files that contain more
+than one image.
+
+To do this:
+
+  Imager->write_multi(\%opts, @images)
+
+Where %opts describes 3 possible types of outputs:
+
+=over 4
+
+=item callback
+
+A code reference which is called with a single parameter, the data to
+be written.  You can also specify $opts{maxbuffer} which is the
+maximum amount of data buffered.  Note that there can be larger writes
+than this if the file library writes larger blocks.  A smaller value
+maybe useful for writing to a socket for incremental display.
+
+=item fd
+
+The file descriptor to save the images to.
+
+=item file
+
+The name of the file to write to.
+
+%opts may also include the keys from L<Gif options> and L<Quantization
+options>.
+
+=back
+
+The current aim is to support other multiple image formats in the
+future, such as TIFF, and to support reading multiple images from a
+single file.
+
+A simple example:
+
+    my @images;
+    # ... code to put images in @images
+    Imager->write_multi({type=>'gif',
+                        file=>'anim.gif',
+                        gif_delays=>[ 10 x @images ] },
+                       @images)
+    or die "Oh dear!";
+
+=head2 Gif options
+
+These options can be specified when calling write_multi() for gif
+files, when writing a single image with the gifquant option set to
+'gen', or for direct calls to i_writegif_gen and i_writegif_callback.
+
+Note that some viewers will ignore some of these options
+(gif_user_input in particular).
+
+=over 4
+
+=item gif_each_palette
+
+Each image in the gif file has it's own palette if this is non-zero.
+All but the first image has a local colour table (the first uses the
+global colour table.
+
+=item interlace
+
+The images are written interlaced if this is non-zero.
+
+=item gif_delays
+
+A reference to an array containing the delays between images, in 1/100
+seconds.
+
+=item gif_user_input
+
+A reference to an array contains user input flags.  If the given flag
+is non-zero the image viewer should wait for input before displaying
+the next image.
+
+=item gif_disposal
+
+A reference to an array of image disposal methods.  These define what
+should be done to the image before displaying the next one.  These are
+integers, where 0 means unspecified, 1 means the image should be left
+in place, 2 means restore to background colour and 3 means restore to
+the previous value.
+
+=item gif_tran_color
+
+A reference to an Imager::Color object, which is the colour to use for
+the palette entry used to represent transparency in the palette.
+
+=item gif_positions
+
+A reference to an array of references to arrays which represent screen
+positions for each image.
+
+=item gif_loop_count
+
+If this is non-zero the Netscape loop extension block is generated,
+which makes the animation of the images repeat.
+
+This is currently unimplemented due to some limitations in giflib.
+
+=back
+
+=head2 Quantization options
+
+These options can be specified when calling write_multi() for gif
+files, when writing a single image with the gifquant option set to
+'gen', or for direct calls to i_writegif_gen and i_writegif_callback.
+
+=over 4
+
+=item colors
+
+A arrayref of colors that are fixed.  Note that some color generators
+will ignore this.
+
+=item transp
+
+The type of transparency processing to perform for images with an
+alpha channel where the output format does not have a proper alpha
+channel (eg. gif).  This can be any of:
+
+=over 4
+
+=item none
+
+No transparency processing is done. (default)
+
+=item threshold
+
+Pixels more transparent that tr_threshold are rendered as transparent.
+
+=item errdiff
+
+An error diffusion dither is done on the alpha channel.  Note that
+this is independent of the translation performed on the colour
+channels, so some combinations may cause undesired artifacts.
+
+=item ordered
+
+The ordered dither specified by tr_orddith is performed on the alpha
+channel.
+
+=back
+
+=item tr_threshold
+
+The highest alpha value at which a pixel will be made transparent when
+transp is 'threshold'. (0-255, default 127)
+
+=item tr_errdiff
+
+The type of error diffusion to perform on the alpha channel when
+transp is 'errdiff'.  This can be any defined error diffusion type
+except for custom (see errdiff below).
+
+=item tr_ordered
+
+The type of ordered dither to perform on the alpha channel when transp
+is 'orddith'.  Possible values are:
+
+=over 4
+
+=item random
+
+A semi-random map is used.  The map is the same each time.  Currently
+the default (which may change.)
+
+=item dot8
+
+8x8 dot dither.
+
+=item dot4
+
+4x4 dot dither
+
+=item hline
+
+horizontal line dither.
+
+=item vline
+
+vertical line dither.
+
+=item "/line"
+
+=item slashline
+
+diagonal line dither
+
+=item '\line'
+
+=item backline
+
+diagonal line dither
+
+=item custom
+
+A custom dither matrix is used - see tr_map
+
+=back
+
+=item tr_map
+
+When tr_orddith is custom this defines an 8 x 8 matrix of integers
+representing the transparency threshold for pixels corresponding to
+each position.  This should be a 64 element array where the first 8
+entries correspond to the first row of the matrix.  Values should be
+betweern 0 and 255.
+
+=item make_colors
+
+Defines how the quantization engine will build the palette(s).
+Currently this is ignored if 'translate' is 'giflib', but that may
+change.  Possible values are:
+
+=over 4
+
+=item none
+
+Only colors supplied in 'colors' are used.
+
+=item webmap
+
+The web color map is used (need url here.)
+
+=item addi
+
+The original code for generating the color map (Addi's code) is used.
+
+=back
+
+Other methods may be added in the future.
+
+=item colors
+
+A arrayref containing Imager::Color objects, which represents the
+starting set of colors to use in translating the images.  webmap will
+ignore this.  The final colors used are copied back into this array
+(which is expanded if necessary.)
+
+=item max_colors
+
+The maximum number of colors to use in the image.
+
+=item translate
+
+The method used to translate the RGB values in the source image into
+the colors selected by make_colors.  Note that make_colors is ignored
+whene translate is 'giflib'.
+
+Possible values are:
+
+=over 4
+
+=item giflib
+
+The giflib native quantization function is used.
+
+=item closest
+
+The closest color available is used.
+
+=item perturb
+
+The pixel color is modified by perturb, and the closest color is chosen.
+
+=item errdiff
+
+An error diffusion dither is performed.
+
+=back
+
+It's possible other transate values will be added.
+
+=item errdiff
+
+The type of error diffusion dither to perform.  These values (except
+for custom) can also be used in tr_errdif.
+
+=over 4
+
+=item floyd
+
+Floyd-Steinberg dither
+
+=item jarvis
+
+Jarvis, Judice and Ninke dither
+
+=item stucki
+
+Stucki dither
+
+=item custom
+
+Custom.  If you use this you must also set errdiff_width,
+errdiff_height and errdiff_map.
+
+=back
+
+=item errdiff_width
+
+=item errdiff_height
+
+=item errdiff_orig
+
+=item errdiff_map
+
+When translate is 'errdiff' and errdiff is 'custom' these define a
+custom error diffusion map.  errdiff_width and errdiff_height define
+the size of the map in the arrayref in errdiff_map.  errdiff_orig is
+an integer which indicates the current pixel position in the top row
+of the map.
+
+=item perturb
+
+When translate is 'perturb' this is the magnitude of the random bias
+applied to each channel of the pixel before it is looked up in the
+color table.
+
+=back
+
+=head2 Obtaining/setting attributes of images
+
+To get the size of an image in pixels the C<$img-E<gt>getwidth()> and
+C<$img-E<gt>getheight()> are used.
+
+To get the number of channels in
+an image C<$img-E<gt>getchannels()> is used.  $img-E<gt>getmask() and
+$img-E<gt>setmask() are used to get/set the channel mask of the image.
+
+  $mask=$img->getmask();
+  $img->setmask(mask=>1+2); # modify red and green only
+  $img->setmask(mask=>8); # modify alpha only
+  $img->setmask(mask=>$mask); # restore previous mask
+
+The mask of an image describes which channels are updated when some
+operation is performed on an image.  Naturally it is not possible to
+apply masks to operations like scaling that alter the dimensions of
+images.
+
+It is possible to have Imager find the number of colors in an image
+by using C<$img-E<gt>getcolorcount()>. It requires memory proportionally
+to the number of colors in the image so it is possible to have it
+stop sooner if you only need to know if there are more than a certain number
+of colors in the image.  If there are more colors than asked for
+the function return undef.  Examples:
+
+  if (!defined($img->getcolorcount(maxcolors=>512)) {
+    print "Less than 512 colors in image\n";
+  }
+
+=head2 Drawing Methods
+
+IMPLEMENTATION MORE OR LESS DONE CHECK THE TESTS
+
+DOCUMENTATION OF THIS SECTION OUT OF SYNC
+
+It is possible to draw with graphics primitives onto images.  Such
+primitives include boxes, arcs, circles and lines.  A reference
+oriented list follows.
+
+Box:
+  $img->box(color=>$blue,xmin=>10,ymin=>30,xmax=>200,ymax=>300,filled=>1);
+
+The above example calls the C<box> method for the image and the box
+covers the pixels with in the rectangle specified.  If C<filled> is
+ommited it is drawn as an outline.  If any of the edges of the box are
+ommited it will snap to the outer edge of the image in that direction.
+Also if a color is omitted a color with (255,255,255,255) is used
+instead.
+
+Arc:
+  $img->arc(color=>$red, r=20, x=>200, y=>100, d1=>10, d2=>20 );
+
+This creates a filled red arc with a 'center' at (200, 100) and spans
+10 degrees and the slice has a radius of 20. SEE section on BUGS.
+
+Circle:
+  $img->circle(color=>$green, r=50, x=>200, y=>100);
+
+This creates a green circle with its center at (200, 100) and has a
+radius of 20.
+
+Line:
+  $img->line(color=>$green, x1=10, x2=>100, 
+                            y1=>20, y2=>50, antialias=>1 );
+
+That draws an antialiased line from (10,100) to (20,50).
+
+Polyline:
+  $img->polyline(points=>[[$x0,$y0],[$x1,$y1],[$x2,$y2]],color=>$red);
+  $img->polyline(x=>[$x0,$x1,$x2], y=>[$y0,$y1,$y2], antialias=>1);
+
+Polyline is used to draw multilple lines between a series of points.
+The point set can either be specified as an arrayref to an array of
+array references (where each such array represents a point).  The
+other way is to specify two array references.
+
+=head2 Text rendering
+
+Text rendering is described in the Imager::Font manpage.
+
+=head2 Image resizing
+
+To scale an image so porportions are maintained use the
+C<$img-E<gt>scale()> method.  if you give either a xpixels or ypixels
+parameter they will determine the width or height respectively.  If
+both are given the one resulting in a larger image is used.  example:
+C<$img> is 700 pixels wide and 500 pixels tall.
+
+  $img->scale(xpixels=>400); # 400x285
+  $img->scale(ypixels=>400); # 560x400
+
+  $img->scale(xpixels=>400,ypixels=>400); # 560x400
+  $img->scale(xpixels=>400,ypixels=>400,type=>min); # 400x285
+
+  $img->scale(scalefactor=>0.25); 175x125 $img->scale(); # 350x250
+
+if you want to create low quality previews of images you can pass
+C<qtype=E<gt>'preview'> to scale and it will use nearest neighbor
+sampling instead of filtering. It is much faster but also generates
+worse looking images - especially if the original has a lot of sharp
+variations and the scaled image is by more than 3-5 times smaller than
+the original.
+
+If you need to scale images per axis it is best to do it simply by
+calling scaleX and scaleY.  You can pass either 'scalefactor' or
+'pixels' to both functions.
+
+Another way to resize an image size is to crop it.  The parameters
+to crop are the edges of the area that you want in the returned image.
+If a parameter is omited a default is used instead.
+
+  $newimg = $img->crop(left=>50, right=>100, top=>10, bottom=>100); 
+  $newimg = $img->crop(left=>50, top=>10, width=>50, height=>90);
+  $newimg = $img->crop(left=>50, right=>100); # top 
+
+You can also specify width and height parameters which will produce a
+new image cropped from the center of the input image, with the given
+width and height.
+
+  $newimg = $img->crop(width=>50, height=>50);
+
+The width and height parameters take precedence over the left/right
+and top/bottom parameters respectively.
+
+=head2 Copying images
+
+To create a copy of an image use the C<copy()> method.  This is usefull
+if you want to keep an original after doing something that changes the image
+inplace like writing text.
+
+  $img=$orig->copy();
+
+To copy an image to onto another image use the C<paste()> method.
+
+  $dest->paste(left=>40,top=>20,img=>$logo);
+
+That copies the entire C<$logo> image onto the C<$dest> image so that the
+upper left corner of the C<$logo> image is at (40,20).
+
+=head2 Blending Images
+To put an image or a part of an image directly into another it is
+best to call the C<paste()> method on the image you want to add to.
+
+  $img->paste(img=>$srcimage,left=>30,top=>50);
+
+That will take paste C<$srcimage> into C<$img> with the upper
+left corner at (30,50).  If no values are given for C<left>
+or C<top> they will default to 0.
+
+A more complicated way of blending images is where one image is 
+put 'over' the other with a certain amount of opaqueness.  The
+method that does this is rubthrough.
+
+  $img->rubthrough(src=>$srcimage,tx=>30,ty=>50); 
+
+That will take the image C<$srcimage> and overlay it with the 
+upper left corner at (30,50).  The C<$srcimage> must be a 4 channel
+image.  The last channel is used as an alpha channel.
+
+
+=head2 Filters
+
+A special image method is the filter method. An example is:
+
+  $img->filter(type=>'autolevels');
+
+This will call the autolevels filter.  Here is a list of the filters
+that are always avaliable in Imager.  This list can be obtained by
+running the C<filterlist.perl> script that comes with the module
+source.
+
+  Filter          Arguments
+  turbnoise
+  autolevels      lsat(0.1) usat(0.1) skew(0)
+  radnoise
+  noise           amount(3) subtype(0)
+  contrast        intensity
+  hardinvert
+  gradgen         xo yo colors dist
+
+The default values are in parenthesis.  All parameters must have some
+value but if a parameter has a default value it may be omitted when
+calling the filter function.
+
+FIXME: make a seperate pod for filters?
+
+=head2 Transformations
+
+Another special image method is transform.  It can be used to generate
+warps and rotations and such features.  It can be given the operations
+in postfix notation or the module Affix::Infix2Postfix can be used.
+Look in the test case t/t55trans.t for an example.
+
+transform() needs expressions (or opcodes) that determine the source
+pixel for each target pixel.  Source expressions are infix expressions
+using any of the +, -, *, / or ** binary operators, the - unary
+operator, ( and ) for grouping and the sin() and cos() functions.  The
+target pixel is input as the variables x and y.
+
+You specify the x and y expressions as xexpr and yexpr respectively.
+You can also specify opcodes directly, but that's magic deep enough
+that you can look at the source code.
+
+You can still use the transform() function, but the transform2()
+function is just as fast and is more likely to be enhanced and
+maintained.
+
+Later versions of Imager also support a transform2() class method
+which allows you perform a more general set of operations, rather than
+just specifying a spatial transformation as with the transform()
+method, you can also perform colour transformations, image synthesis
+and image combinations.
+
+transform2() takes an reference to an options hash, and a list of
+images to operate one (this list may be empty):
+
+  my %opts;
+  my @imgs;
+  ...
+  my $img = Imager::transform2(\%opts, @imgs)
+      or die "transform2 failed: $Imager::ERRSTR";
+
+The options hash may define a transformation function, and optionally:
+
+=over 4
+
+=item *
+
+width - the width of the image in pixels.  If this isn't supplied the
+width of the first input image is used.  If there are no input images
+an error occurs.
+
+=item *
+
+height - the height of the image in pixels.  If this isn't supplied
+the height of the first input image is used.  If there are no input
+images an error occurs.
+
+=item *
+
+constants - a reference to hash of constants to define for the
+expression engine.  Some extra constants are defined by Imager
+
+=back
+
+The tranformation function is specified using either the expr or
+rpnexpr member of the options.
+
+=over 4
+
+=item Infix expressions
+
+You can supply infix expressions to transform 2 with the expr keyword.
+
+$opts{expr} = 'return getp1(w-x, h-y)'
+
+The 'expression' supplied follows this general grammar:
+
+   ( identifier '=' expr ';' )* 'return' expr
+
+This allows you to simplify your expressions using variables.
+
+A more complex example might be:
+
+$opts{expr} = 'pix = getp1(x,y); return if(value(pix)>0.8,pix*0.8,pix)'
+
+Currently to use infix expressions you must have the Parse::RecDescent
+module installed (available from CPAN).  There is also what might be a
+significant delay the first time you run the infix expression parser
+due to the compilation of the expression grammar.
+
+=item Postfix expressions
+
+You can supply postfix or reverse-polish notation expressions to
+transform2() through the rpnexpr keyword.
+
+The parser for rpnexpr emulates a stack machine, so operators will
+expect to see their parameters on top of the stack.  A stack machine
+isn't actually used during the image transformation itself.
+
+You can store the value at the top of the stack in a variable called
+foo using !foo and retrieve that value again using @foo.  The !foo
+notation will pop the value from the stack.
+
+An example equivalent to the infix expression above:
+
+ $opts{rpnexpr} = 'x y getp1 !pix @pix value 0.8 gt @pix 0.8 * @pix ifp'
+
+=back
+
+transform2() has a fairly rich range of operators.
+
+=over 4
+
+=item +, *, -, /, %, **
+
+multiplication, addition, subtraction, division, remainder and
+exponentiation.  Multiplication, addition and subtraction can be used
+on colour values too - though you need to be careful - adding 2 white
+values together and multiplying by 0.5 will give you grey, not white.
+
+Division by zero (or a small number) just results in a large number.
+Modulo zero (or a small number) results in zero.
+
+=item sin(N), cos(N), atan2(y,x)
+
+Some basic trig functions.  They work in radians, so you can't just
+use the hue values.
+
+=item distance(x1, y1, x2, y2)
+
+Find the distance between two points.  This is handy (along with
+atan2()) for producing circular effects.
+
+=item sqrt(n)
+
+Find the square root.  I haven't had much use for this since adding
+the distance() function.
+
+=item abs(n)
+
+Find the absolute value.
+
+=item getp1(x,y), getp2(x,y), getp3(x, y)
+
+Get the pixel at position (x,y) from the first, second or third image
+respectively.  I may add a getpn() function at some point, but this
+prevents static checking of the instructions against the number of
+images actually passed in.
+
+=item value(c), hue(c), sat(c), hsv(h,s,v)
+
+Separates a colour value into it's value (brightness), hue (colour)
+and saturation elements.  Use hsv() to put them back together (after
+suitable manipulation).
+
+=item red(c), green(c), blue(c), rgb(r,g,b)
+
+Separates a colour value into it's red, green and blue colours.  Use
+rgb(r,g,b) to put it back together.
+
+=item int(n)
+
+Convert a value to an integer.  Uses a C int cast, so it may break on
+large values.
+
+=item if(cond,ntrue,nfalse), if(cond,ctrue,cfalse)
+
+A simple (and inefficient) if function.
+
+=item <=,<,==,>=,>,!=
+
+Relational operators (typically used with if()).  Since we're working
+with floating point values the equalities are 'near equalities' - an
+epsilon value is used.
+
+=item &&, ||, not(n)
+
+Basic logical operators.
+
+=back
+
+A few examples:
+
+=over 4
+
+=item rpnexpr=>'x 25 % 15 * y 35 % 10 * getp1 !pat x y getp1 !pix @pix sat 0.7 gt @pat @pix ifp'
+
+tiles a smaller version of the input image over itself where the colour has a saturation over 0.7.
+
+=item rpnexpr=>'x 25 % 15 * y 35 % 10 * getp1 !pat y 360 / !rat x y getp1 1 @rat - pmult @pat @rat pmult padd'
+
+tiles the input image over itself so that at the top of the image the
+full-size image is at full strength and at the bottom the tiling is
+most visible.
+
+=item rpnexpr=>'x y getp1 !pix @pix value 0.96 gt @pix sat 0.1 lt and 128 128 255 rgb @pix ifp'
+
+replace pixels that are white or almost white with a palish blue
+
+=item rpnexpr=>'x 35 % 10 * y 45 % 8 * getp1 !pat x y getp1 !pix @pix sat 0.2 lt @pix value 0.9 gt and @pix @pat @pix value 2 / 0.5 + pmult ifp'
+
+Tiles the input image overitself where the image isn't white or almost
+white.
+
+=item rpnexpr=>'x y 160 180 distance !d y 180 - x 160 - atan2 !a @d 10 / @a + 3.1416 2 * % !a2 @a2 180 * 3.1416 / 1 @a2 sin 1 + 2 / hsv'
+
+Produces a spiral.
+
+=item rpnexpr=>'x y 160 180 distance !d y 180 - x 160 - atan2 !a @d 10 / @a + 3.1416 2 * % !a2 @a 180 * 3.1416 / 1 @a2 sin 1 + 2 / hsv'
+
+A spiral built on top of a colour wheel.
+
+=back
+
+For details on expression parsing see L<Imager::Expr>.  For details on
+the virtual machine used to transform the images, see
+L<Imager::regmach.pod>.
+
+=head2 Plugins
+
+It is possible to add filters to the module without recompiling the
+module itself.  This is done by using DSOs (Dynamic shared object)
+avaliable on most systems.  This way you can maintain our own filters
+and not have to get me to add it, or worse patch every new version of
+the Module.  Modules can be loaded AND UNLOADED at runtime.  This
+means that you can have a server/daemon thingy that can do something
+like:
+
+  load_plugin("dynfilt/dyntest.so")  || die "unable to load plugin\n";
+  %hsh=(a=>35,b=>200,type=>lin_stretch);
+  $img->filter(%hsh);
+  unload_plugin("dynfilt/dyntest.so") || die "unable to load plugin\n";
+  $img->write(type=>'pnm',file=>'testout/t60.jpg')
+    || die "error in write()\n";
+
+Someone decides that the filter is not working as it should -
+dyntest.c modified and recompiled.
+
+  load_plugin("dynfilt/dyntest.so") || die "unable to load plugin\n";
+  $img->filter(%hsh); 
+
+An example plugin comes with the module - Please send feedback to 
+addi@umich.edu if you test this.
+
+Note: This seems to test ok on the following systems:
+Linux, Solaris, HPUX, OpenBSD, FreeBSD, TRU64/OSF1, AIX.
+If you test this on other systems please let me know.
+
+=head1 BUGS
+
+box, arc, circle do not support antialiasing yet.  arc, is only filled
+as of yet.  Some routines do not return $self where they should.  This
+affects code like this, C<$img-E<gt>box()-E<gt>arc()> where an object
+is expected.
+
+When saving Gif images the program does NOT try to shave of extra
+colors if it is possible.  If you specify 128 colors and there are
+only 2 colors used - it will have a 128 colortable anyway.
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson, addi@umich.edu
+And a great deal of help from others - see the README for a complete
+list.
+=head1 SEE ALSO
+
+perl(1), Imager::Color(3), Affix::Infix2Postfix(3), Parse::RecDescent(3)
+http://www.eecs.umich.edu/~addi/perl/Imager/
+
+
+=cut
diff --git a/Imager.xs b/Imager.xs
new file mode 100644 (file)
index 0000000..e702bf1
--- /dev/null
+++ b/Imager.xs
@@ -0,0 +1,1813 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "ppport.h"
+#ifdef __cplusplus
+
+#endif
+
+#include "image.h"
+#include "feat.h"
+#include "dynaload.h"
+#include "regmach.h"
+
+typedef io_glue* Imager__IO;
+typedef i_color* Imager__Color;
+typedef i_img*   Imager__ImgRaw;
+
+
+#ifdef HAVE_LIBTT
+typedef TT_Fonthandle* Imager__TTHandle;
+#endif
+
+typedef struct i_reader_data_tag
+{
+  /* presumably a CODE ref or name of a sub */
+  SV *sv;
+} i_reader_data;
+
+/* used by functions that want callbacks */
+static int read_callback(char *userdata, char *buffer, int need, int want) {
+  i_reader_data *rd = (i_reader_data *)userdata;
+  int count;
+  int result;
+  SV *data;
+  dSP; dTARG = sv_newmortal();
+  /* thanks to Simon Cozens for help with the dTARG above */
+
+  ENTER;
+  SAVETMPS;
+  EXTEND(SP, 2);
+  PUSHMARK(SP);
+  PUSHi(want);
+  PUSHi(need);
+  PUTBACK;
+
+  count = perl_call_sv(rd->sv, G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+  data = POPs;
+
+  if (SvOK(data)) {
+    STRLEN len;
+    char *ptr = SvPV(data, len);
+    if (len > want)
+      croak("Too much data returned in reader callback");
+    
+    memcpy(buffer, ptr, len);
+    result = len;
+  }
+  else {
+    result = -1;
+  }
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return result;
+}
+
+typedef struct
+{
+  SV *sv; /* a coderef or sub name */
+} i_writer_data;
+
+/* used by functions that want callbacks */
+static int write_callback(char *userdata, char const *data, int size) {
+  i_writer_data *wd = (i_writer_data *)userdata;
+  int count;
+  int success;
+  SV *sv;
+  dSP; 
+
+  ENTER;
+  SAVETMPS;
+  EXTEND(SP, 1);
+  PUSHMARK(SP);
+  XPUSHs(sv_2mortal(newSVpv((char *)data, size)));
+  PUTBACK;
+
+  count = perl_call_sv(wd->sv, G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+  sv = POPs;
+  success = SvTRUE(sv);
+
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return success;
+}
+
+struct value_name {
+  char *name;
+  int value;
+};
+static int lookup_name(struct value_name *names, int count, char *name, int def_value)
+{
+  int i;
+  for (i = 0; i < count; ++i)
+    if (strEQ(names[i].name, name))
+      return names[i].value;
+
+  return def_value;
+}
+static struct value_name transp_names[] =
+{
+  { "none", tr_none },
+  { "threshold", tr_threshold },
+  { "errdiff", tr_errdiff },
+  { "ordered", tr_ordered, },
+};
+
+static struct value_name make_color_names[] =
+{
+  { "none", mc_none, },
+  { "webmap", mc_web_map, },
+  { "addi", mc_addi, },
+};
+
+static struct value_name translate_names[] =
+{
+#ifdef HAVE_LIBGIF
+  { "giflib", pt_giflib, },
+#endif
+  { "closest", pt_closest, },
+  { "perturb", pt_perturb, },
+  { "errdiff", pt_errdiff, },
+};
+
+static struct value_name errdiff_names[] =
+{
+  { "floyd", ed_floyd, },
+  { "jarvis", ed_jarvis, },
+  { "stucki", ed_stucki, },
+  { "custom", ed_custom, },
+};
+
+static struct value_name orddith_names[] =
+{
+  { "random", od_random, },
+  { "dot8", od_dot8, },
+  { "dot4", od_dot4, },
+  { "hline", od_hline, },
+  { "vline", od_vline, },
+  { "/line", od_slashline, },
+  { "slashline", od_slashline, },
+  { "\\line", od_backline, },
+  { "backline", od_backline, },
+  { "custom", od_custom, },
+};
+
+/* look through the hash for quantization options */
+static void handle_quant_opts(i_quantize *quant, HV *hv)
+{
+  /*** POSSIBLY BROKEN: do I need to unref the SV from hv_fetch ***/
+  SV **sv;
+  int i;
+  STRLEN len;
+  char *str;
+
+  sv = hv_fetch(hv, "transp", 6, 0);
+  if (sv && *sv && (str = SvPV(*sv, len))) {
+    quant->transp = 
+      lookup_name(transp_names, sizeof(transp_names)/sizeof(*transp_names), 
+                 str, tr_none);
+    if (quant->transp != tr_none) {
+      quant->tr_threshold = 127;
+      sv = hv_fetch(hv, "tr_threshold", 12, 0);
+      if (sv && *sv)
+       quant->tr_threshold = SvIV(*sv);
+    }
+    if (quant->transp == tr_errdiff) {
+      sv = hv_fetch(hv, "tr_errdiff", 10, 0);
+      if (sv && *sv && (str = SvPV(*sv, len)))
+       quant->tr_errdiff = lookup_name(errdiff_names, sizeof(errdiff_names)/sizeof(*errdiff_names), str, ed_floyd);
+    }
+    if (quant->transp == tr_ordered) {
+      quant->tr_orddith = od_random;
+      sv = hv_fetch(hv, "tr_orddith", 10, 0);
+      if (sv && *sv && (str = SvPV(*sv, len)))
+       quant->tr_orddith = lookup_name(orddith_names, sizeof(orddith_names)/sizeof(*orddith_names), str, od_random);
+
+      if (quant->tr_orddith == od_custom) {
+       sv = hv_fetch(hv, "tr_map", 6, 0);
+       if (sv && *sv && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+         AV *av = (AV*)SvRV(*sv);
+         len = av_len(av) + 1;
+         if (len > sizeof(quant->tr_custom))
+           len = sizeof(quant->tr_custom);
+         for (i = 0; i < len; ++i) {
+           SV **sv2 = av_fetch(av, i, 0);
+           if (sv2 && *sv2) {
+             quant->tr_custom[i] = SvIV(*sv2);
+           }
+         }
+         while (i < sizeof(quant->tr_custom))
+           quant->tr_custom[i++] = 0;
+       }
+      }
+    }
+  }
+  quant->make_colors = mc_addi;
+  sv = hv_fetch(hv, "make_colors", 11, 0);
+  if (sv && *sv && (str = SvPV(*sv, len))) {
+    quant->make_colors = 
+      lookup_name(make_color_names, sizeof(make_color_names)/sizeof(*make_color_names), str, mc_addi);
+  }
+  sv = hv_fetch(hv, "colors", 6, 0);
+  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+    /* needs to be an array of Imager::Color
+       note that the caller allocates the mc_color array and sets mc_size
+       to it's size */
+    AV *av = (AV *)SvRV(*sv);
+    quant->mc_count = av_len(av)+1;
+    if (quant->mc_count > quant->mc_size)
+      quant->mc_count = quant->mc_size;
+    for (i = 0; i < quant->mc_count; ++i) {
+      SV **sv1 = av_fetch(av, i, 0);
+      if (sv1 && *sv1 && SvROK(*sv1) && sv_derived_from(*sv1, "Imager::Color")) {
+       i_color *col = (i_color *)SvIV((SV*)SvRV(*sv1));
+       quant->mc_colors[i] = *col;
+      }
+    }
+  }
+  sv = hv_fetch(hv, "max_colors", 10, 0);
+  if (sv && *sv) {
+    i = SvIV(*sv);
+    if (i <= quant->mc_size && i >= quant->mc_count)
+      quant->mc_size = i;
+  }
+
+  quant->translate = pt_closest;
+  sv = hv_fetch(hv, "translate", 9, 0);
+  if (sv && *sv && (str = SvPV(*sv, len))) {
+    quant->translate = lookup_name(translate_names, sizeof(translate_names)/sizeof(*translate_names), str, pt_closest);
+  }
+  sv = hv_fetch(hv, "errdiff", 7, 0);
+  if (sv && *sv && (str = SvPV(*sv, len))) {
+    quant->errdiff = lookup_name(errdiff_names, sizeof(errdiff_names)/sizeof(*errdiff_names), str, ed_floyd);
+  }
+  if (quant->translate == pt_errdiff && quant->errdiff == ed_custom) {
+    /* get the error diffusion map */
+    sv = hv_fetch(hv, "errdiff_width", 13, 0);
+    if (sv && *sv)
+      quant->ed_width = SvIV(*sv);
+    sv = hv_fetch(hv, "errdiff_height", 14, 0);
+    if (sv && *sv)
+      quant->ed_height = SvIV(*sv);
+    sv = hv_fetch(hv, "errdiff_orig", 12, 0);
+    if (sv && *sv)
+      quant->ed_orig = SvIV(*sv);
+    if (quant->ed_width > 0 && quant->ed_height > 0) {
+      int sum = 0;
+      quant->ed_map = mymalloc(sizeof(int)*quant->ed_width*quant->ed_height);
+      sv = hv_fetch(hv, "errdiff_map", 11, 0);
+      if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+       AV *av = (AV*)SvRV(*sv);
+       len = av_len(av) + 1;
+       if (len > quant->ed_width * quant->ed_height)
+         len = quant->ed_width * quant->ed_height;
+       for (i = 0; i < len; ++i) {
+         SV **sv2 = av_fetch(av, i, 0);
+         if (sv2 && *sv2) {
+           quant->ed_map[i] = SvIV(*sv2);
+           sum += quant->ed_map[i];
+         }
+       }
+      }
+      if (!sum) {
+       /* broken map */
+       myfree(quant->ed_map);
+       quant->ed_map = 0;
+       quant->errdiff = ed_floyd;
+      }
+    }
+  }
+  sv = hv_fetch(hv, "perturb", 7, 0);
+  if (sv && *sv)
+    quant->perturb = SvIV(*sv);
+}
+
+/* look through the hash for options to add to opts */
+static void handle_gif_opts(i_gif_opts *opts, HV *hv)
+{
+  /*** FIXME: POSSIBLY BROKEN: do I need to unref the SV from hv_fetch? ***/
+  SV **sv;
+  int i;
+  /**((char *)0) = '\0';*/
+  sv = hv_fetch(hv, "gif_each_palette", 16, 0);
+  if (sv && *sv)
+    opts->each_palette = SvIV(*sv);
+  sv = hv_fetch(hv, "interlace", 9, 0);
+  if (sv && *sv)
+    opts->interlace = SvIV(*sv);
+  sv = hv_fetch(hv, "gif_delays", 10, 0);
+  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+    AV *av = (AV*)SvRV(*sv);
+    opts->delay_count = av_len(av)+1;
+    opts->delays = mymalloc(sizeof(int) * opts->delay_count);
+    for (i = 0; i < opts->delay_count; ++i) {
+      SV *sv1 = *av_fetch(av, i, 0);
+      opts->delays[i] = SvIV(sv1);
+    }
+  }
+  sv = hv_fetch(hv, "gif_user_input", 14, 0);
+  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+    AV *av = (AV*)SvRV(*sv);
+    opts->user_input_count = av_len(av)+1;
+    opts->user_input_flags = mymalloc(opts->user_input_count);
+    for (i = 0; i < opts->user_input_count; ++i) {
+      SV *sv1 = *av_fetch(av, i, 0);
+      opts->user_input_flags[i] = SvIV(sv1) != 0;
+    }
+  }
+  sv = hv_fetch(hv, "gif_disposal", 12, 0);
+  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+    AV *av = (AV*)SvRV(*sv);
+    opts->disposal_count = av_len(av)+1;
+    opts->disposal = mymalloc(opts->disposal_count);
+    for (i = 0; i < opts->disposal_count; ++i) {
+      SV *sv1 = *av_fetch(av, i, 0);
+      opts->disposal[i] = SvIV(sv1);
+    }
+  }
+  sv = hv_fetch(hv, "gif_tran_color", 14, 0);
+  if (sv && *sv && SvROK(*sv) && sv_derived_from(*sv, "Imager::Color")) {
+    i_color *col = (i_color *)SvIV((SV *)SvRV(*sv));
+    opts->tran_color = *col;
+  }
+  sv = hv_fetch(hv, "gif_positions", 13, 0);
+  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+    AV *av = (AV *)SvRV(*sv);
+    opts->position_count = av_len(av) + 1;
+    opts->positions = mymalloc(sizeof(i_gif_pos) * opts->position_count);
+    for (i = 0; i < opts->position_count; ++i) {
+      SV **sv2 = av_fetch(av, i, 0);
+      opts->positions[i].x = opts->positions[i].y = 0;
+      if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
+       AV *av2 = (AV*)SvRV(*sv2);
+       SV **sv3;
+       sv3 = av_fetch(av2, 0, 0);
+       if (sv3 && *sv3)
+         opts->positions[i].x = SvIV(*sv3);
+       sv3 = av_fetch(av2, 1, 0);
+       if (sv3 && *sv3)
+         opts->positions[i].y = SvIV(*sv3);
+      }
+    }
+  }
+  /* Netscape2.0 loop count extension */
+  sv = hv_fetch(hv, "gif_loop_count", 14, 0);
+  if (sv && *sv)
+    opts->loop_count = SvIV(*sv);
+}
+
+/* copies the color map from the hv into the colors member of the HV */
+static void copy_colors_back(HV *hv, i_quantize *quant) {
+  SV **sv;
+  AV *av;
+  int i;
+  SV *work;
+
+  sv = hv_fetch(hv, "colors", 6, 0);
+  if (!sv || !*sv || !SvROK(*sv) || SvTYPE(SvRV(*sv)) != SVt_PVAV) {
+    SV *ref;
+    av = newAV();
+    ref = newRV_inc((SV*) av);
+    sv = hv_store(hv, "colors", 6, ref, 0);
+  }
+  else {
+    av = (AV *)SvRV(*sv);
+  }
+  av_extend(av, quant->mc_count+1);
+  for (i = 0; i < quant->mc_count; ++i) {
+    i_color *in = quant->mc_colors+i;
+    Imager__Color c = ICL_new_internal(in->rgb.r, in->rgb.g, in->rgb.b, 255);
+    work = sv_newmortal();
+    sv_setref_pv(work, "Imager::Color", (void *)c);
+    SvREFCNT_inc(work);
+    if (!av_store(av, i, work)) {
+      SvREFCNT_dec(work);
+    }
+  }
+}
+
+/* used to make the typemap happy */
+typedef HV *HASH;
+
+MODULE = Imager                PACKAGE = Imager::Color PREFIX = ICL_
+
+Imager::Color
+ICL_new_internal(r,g,b,a)
+               unsigned char     r
+               unsigned char     g
+               unsigned char     b
+               unsigned char     a
+
+void
+ICL_DESTROY(cl)
+               Imager::Color    cl
+
+
+Imager::Color
+ICL_set_internal(cl,r,g,b,a)
+               Imager::Color    cl
+               unsigned char     r
+               unsigned char     g
+               unsigned char     b
+               unsigned char     a
+
+
+void
+ICL_info(cl)
+               Imager::Color    cl
+
+
+void
+ICL_rgba(cl)
+             Imager::Color     cl
+           PPCODE:
+               EXTEND(SP, 4);
+               PUSHs(sv_2mortal(newSVnv(cl->rgba.r)));
+               PUSHs(sv_2mortal(newSVnv(cl->rgba.g)));
+               PUSHs(sv_2mortal(newSVnv(cl->rgba.b)));
+               PUSHs(sv_2mortal(newSVnv(cl->rgba.a)));
+
+
+
+
+
+
+
+MODULE = Imager                PACKAGE = Imager::ImgRaw        PREFIX = IIM_
+
+Imager::ImgRaw
+IIM_new(x,y,ch)
+               int     x
+              int     y
+              int     ch
+
+void
+IIM_DESTROY(im)
+               Imager::ImgRaw    im
+
+
+
+MODULE = Imager                PACKAGE = Imager
+
+PROTOTYPES: ENABLE
+
+
+Imager::IO
+io_new_fd(fd)
+                         int     fd
+
+Imager::IO
+io_new_bufchain()
+
+
+void
+io_slurp(ig)
+        Imager::IO     ig
+            PREINIT:
+             unsigned char*    data;
+            size_t    tlength;
+                SV*    r;
+            PPCODE:
+             data    = NULL;
+              tlength = io_slurp(ig, &data);
+              r = sv_newmortal();
+              EXTEND(SP,1);
+              PUSHs(sv_2mortal(newSVpv(data,tlength)));
+              myfree(data);
+
+
+
+void
+i_list_formats()
+            PREINIT:
+             char*    item;
+              int     i;
+            PPCODE:
+              i=0;
+              while( (item=i_format_list[i++]) != NULL ) {
+                     EXTEND(SP, 1);
+                     PUSHs(sv_2mortal(newSVpv(item,0)));
+              }
+
+undef_int
+i_has_format(frmt)
+              char*    frmt
+
+Imager::ImgRaw
+i_img_new()
+
+Imager::ImgRaw
+i_img_empty(im,x,y)
+    Imager::ImgRaw     im
+               int     x
+              int     y
+
+Imager::ImgRaw
+i_img_empty_ch(im,x,y,ch)
+    Imager::ImgRaw     im
+               int     x
+              int     y
+              int     ch
+
+void
+init_log(name,level)
+             char*    name
+              int     level
+
+void
+i_img_exorcise(im)
+    Imager::ImgRaw     im
+
+void
+i_img_destroy(im)
+    Imager::ImgRaw     im
+
+void
+i_img_info(im)
+    Imager::ImgRaw     im
+            PREINIT:
+              int     info[4];
+            PPCODE:
+              i_img_info(im,info);
+               EXTEND(SP, 4);
+               PUSHs(sv_2mortal(newSViv(info[0])));
+               PUSHs(sv_2mortal(newSViv(info[1])));
+               PUSHs(sv_2mortal(newSViv(info[2])));
+               PUSHs(sv_2mortal(newSViv(info[3])));
+
+
+
+
+void
+i_img_setmask(im,ch_mask)
+    Imager::ImgRaw     im
+              int     ch_mask
+
+int
+i_img_getmask(im)
+    Imager::ImgRaw     im
+
+int
+i_img_getchannels(im)
+    Imager::ImgRaw     im
+
+void
+i_img_getdata(im)
+    Imager::ImgRaw     im
+             PPCODE:
+              EXTEND(SP, 1);
+               PUSHs(sv_2mortal(newSVpv(im->data, im->bytes)));
+
+
+void
+i_draw(im,x1,y1,x2,y2,val)
+    Imager::ImgRaw     im
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+     Imager::Color     val
+
+void
+i_line_aa(im,x1,y1,x2,y2,val)
+    Imager::ImgRaw     im
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+     Imager::Color     val
+
+void
+i_box(im,x1,y1,x2,y2,val)
+    Imager::ImgRaw     im
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+     Imager::Color     val
+
+void
+i_box_filled(im,x1,y1,x2,y2,val)
+    Imager::ImgRaw     im
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+          Imager::Color    val
+
+void
+i_arc(im,x,y,rad,d1,d2,val)
+    Imager::ImgRaw     im
+              int     x
+              int     y
+             float     rad
+             float     d1
+             float     d2
+          Imager::Color    val
+
+
+
+void
+i_bezier_multi(im,xc,yc,val)
+    Imager::ImgRaw     im
+             Imager::Color  val
+            PREINIT:
+            double   *x,*y;
+            int       len;
+            AV       *av1;
+            AV       *av2;
+            SV       *sv1;
+            SV       *sv2;
+            int i;
+            PPCODE:
+            ICL_info(val);
+            if (!SvROK(ST(1))) croak("Imager: Parameter 1 to i_bezier_multi must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 to i_bezier_multi must be a reference to an array\n");
+            if (!SvROK(ST(2))) croak("Imager: Parameter 2 to i_bezier_multi must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 2 to i_bezier_multi must be a reference to an array\n");
+            av1=(AV*)SvRV(ST(1));
+            av2=(AV*)SvRV(ST(2));
+            if (av_len(av1) != av_len(av2)) croak("Imager: x and y arrays to i_bezier_multi must be equal length\n");
+            len=av_len(av1)+1;
+            x=mymalloc( len*sizeof(double) );
+            y=mymalloc( len*sizeof(double) );
+            for(i=0;i<len;i++) {
+              sv1=(*(av_fetch(av1,i,0)));
+              sv2=(*(av_fetch(av2,i,0)));
+              x[i]=(double)SvNV(sv1);
+              y[i]=(double)SvNV(sv2);
+            }
+             i_bezier_multi(im,len,x,y,val);
+             myfree(x);
+             myfree(y);
+
+
+void
+i_poly_aa(im,xc,yc,val)
+    Imager::ImgRaw     im
+             Imager::Color  val
+            PREINIT:
+            double   *x,*y;
+            int       len;
+            AV       *av1;
+            AV       *av2;
+            SV       *sv1;
+            SV       *sv2;
+            int i;
+            PPCODE:
+            ICL_info(val);
+            if (!SvROK(ST(1))) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
+            if (!SvROK(ST(2))) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
+            av1=(AV*)SvRV(ST(1));
+            av2=(AV*)SvRV(ST(2));
+            if (av_len(av1) != av_len(av2)) croak("Imager: x and y arrays to i_poly_aa must be equal length\n");
+            len=av_len(av1)+1;
+            x=mymalloc( len*sizeof(double) );
+            y=mymalloc( len*sizeof(double) );
+            for(i=0;i<len;i++) {
+              sv1=(*(av_fetch(av1,i,0)));
+              sv2=(*(av_fetch(av2,i,0)));
+              x[i]=(double)SvNV(sv1);
+              y[i]=(double)SvNV(sv2);
+            }
+             i_poly_aa(im,len,x,y,val);
+             myfree(x);
+             myfree(y);
+
+
+
+void
+i_flood_fill(im,seedx,seedy,dcol)
+    Imager::ImgRaw     im
+              int     seedx
+              int     seedy
+     Imager::Color     dcol
+
+
+void
+i_copyto(im,src,x1,y1,x2,y2,tx,ty)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     src
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+              int     tx
+              int     ty
+
+
+void
+i_copyto_trans(im,src,x1,y1,x2,y2,tx,ty,trans)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     src
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+              int     tx
+              int     ty
+     Imager::Color     trans
+
+void
+i_copy(im,src)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     src
+
+
+void
+i_rubthru(im,src,tx,ty)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     src
+              int     tx
+              int     ty
+
+
+void
+i_gaussian(im,stdev)
+    Imager::ImgRaw     im
+            float     stdev
+
+void
+i_conv(im,pcoef)
+    Imager::ImgRaw     im
+            PREINIT:
+            float*    coeff;
+            int     len;
+            AV* av;
+            SV* sv1;
+            int i;
+            PPCODE:
+            if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 must be a reference to an array\n");
+            av=(AV*)SvRV(ST(1));
+            len=av_len(av)+1;
+            coeff=mymalloc( len*sizeof(float) );
+            for(i=0;i<len;i++) {
+              sv1=(*(av_fetch(av,i,0)));
+              coeff[i]=(float)SvNV(sv1);
+            }
+            i_conv(im,coeff,len);
+            myfree(coeff);
+
+                 
+float
+i_img_diff(im1,im2)
+    Imager::ImgRaw     im1
+    Imager::ImgRaw     im2
+
+
+
+undef_int        
+i_init_fonts()
+
+#ifdef HAVE_LIBT1
+
+void
+i_t1_set_aa(st)
+              int     st
+
+int
+i_t1_new(pfb,afm=NULL)
+                     char*    pfb
+                     char*    afm
+
+int
+i_t1_destroy(font_id)
+                      int     font_id
+
+
+undef_int
+i_t1_cp(im,xb,yb,channel,fontnum,points,str,len,align)
+    Imager::ImgRaw     im
+              int     xb
+              int     yb
+              int     channel
+              int     fontnum
+             float     points
+             char*    str
+              int     len
+              int     align
+
+void
+i_t1_bbox(fontnum,point,str,len)
+               int     fontnum
+            float     point
+             char*    str
+              int     len
+            PREINIT:
+              int     cords[6];
+            PPCODE:
+              i_t1_bbox(fontnum,point,str,len,cords);
+               EXTEND(SP, 4);
+               PUSHs(sv_2mortal(newSViv(cords[0])));
+               PUSHs(sv_2mortal(newSViv(cords[1])));
+               PUSHs(sv_2mortal(newSViv(cords[2])));
+               PUSHs(sv_2mortal(newSViv(cords[3])));
+               PUSHs(sv_2mortal(newSViv(cords[4])));
+               PUSHs(sv_2mortal(newSViv(cords[5])));
+
+
+
+undef_int
+i_t1_text(im,xb,yb,cl,fontnum,points,str,len,align)
+    Imager::ImgRaw     im
+              int     xb
+              int     yb
+     Imager::Color    cl
+              int     fontnum
+             float     points
+             char*    str
+              int     len
+              int     align
+
+#endif 
+
+#ifdef HAVE_LIBTT
+
+
+Imager::TTHandle
+i_tt_new(fontname)
+             char*     fontname
+
+void
+i_tt_destroy(handle)
+     Imager::TTHandle    handle
+
+
+
+undef_int
+i_tt_text(handle,im,xb,yb,cl,points,str,len,smooth)
+  Imager::TTHandle     handle
+    Imager::ImgRaw     im
+              int     xb
+              int     yb
+     Imager::Color     cl
+             float     points
+             char*    str
+              int     len
+              int     smooth
+
+
+undef_int
+i_tt_cp(handle,im,xb,yb,channel,points,str,len,smooth)
+  Imager::TTHandle     handle
+    Imager::ImgRaw     im
+              int     xb
+              int     yb
+              int     channel
+             float     points
+             char*    str
+              int     len
+              int     smooth
+
+
+
+undef_int
+i_tt_bbox(handle,point,str,len)
+  Imager::TTHandle     handle
+            float     point
+             char*    str
+              int     len
+            PREINIT:
+              int     cords[6],rc;
+            PPCODE:
+              if ((rc=i_tt_bbox(handle,point,str,len,cords))) {
+                 EXTEND(SP, 4);
+                 PUSHs(sv_2mortal(newSViv(cords[0])));
+                 PUSHs(sv_2mortal(newSViv(cords[1])));
+                 PUSHs(sv_2mortal(newSViv(cords[2])));
+                 PUSHs(sv_2mortal(newSViv(cords[3])));
+                 PUSHs(sv_2mortal(newSViv(cords[4])));
+                 PUSHs(sv_2mortal(newSViv(cords[5])));
+               }
+
+
+#endif 
+
+
+
+
+#ifdef HAVE_LIBJPEG
+undef_int
+i_writejpeg(im,fd,qfactor)
+    Imager::ImgRaw     im
+              int     fd
+              int     qfactor
+
+void
+i_readjpeg(fd)
+              int     fd
+            PREINIT:
+             char*    iptc_itext;
+              int     tlength;
+            i_img*    rimg;
+                SV*    r;
+            PPCODE:
+             iptc_itext = NULL;
+             rimg=i_readjpeg(fd,&iptc_itext,&tlength);
+             if (iptc_itext == NULL) {
+                   r = sv_newmortal();
+                   EXTEND(SP,1);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+             } else {
+                   r = sv_newmortal();
+                   EXTEND(SP,2);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+                   PUSHs(sv_2mortal(newSVpv(iptc_itext,tlength)));
+                    myfree(iptc_itext);
+             }
+
+
+void
+i_readjpeg_scalar(...)
+          PROTOTYPE: $
+            PREINIT:
+             char*    data;
+      unsigned int     length;
+             char*    iptc_itext;
+              int     tlength;
+            i_img*    rimg;
+                SV*    r;
+            PPCODE:
+             iptc_itext = NULL;
+              data = (char *)SvPV(ST(0), length);
+             rimg=i_readjpeg_scalar(data,length,&iptc_itext,&tlength);
+             mm_log((1,"i_readjpeg_scalar: 0x%08X\n",rimg));
+             if (iptc_itext == NULL) {
+                   r = sv_newmortal();
+                   EXTEND(SP,1);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+             } else {
+                   r = sv_newmortal();
+                   EXTEND(SP,2);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+                   PUSHs(sv_2mortal(newSVpv(iptc_itext,tlength)));
+                    myfree(iptc_itext);
+             }
+
+
+void
+i_readjpeg_wiol(ig)
+        Imager::IO     ig
+            PREINIT:
+             char*    iptc_itext;
+              int     tlength;
+            i_img*    rimg;
+                SV*    r;
+            PPCODE:
+             iptc_itext = NULL;
+             rimg = i_readjpeg_wiol(ig,-1,&iptc_itext,&tlength);
+             if (iptc_itext == NULL) {
+                   r = sv_newmortal();
+                   EXTEND(SP,1);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+             } else {
+                   r = sv_newmortal();
+                   EXTEND(SP,2);
+                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+                   PUSHs(r);
+                   PUSHs(sv_2mortal(newSVpv(iptc_itext,tlength)));
+                    myfree(iptc_itext);
+             }
+
+
+#endif
+
+
+
+
+#ifdef HAVE_LIBTIFF
+
+Imager::ImgRaw
+i_readtiff_wiol(ig, length)
+        Imager::IO     ig
+              int     length
+
+
+undef_int
+i_writetiff_wiol(im, ig)
+    Imager::ImgRaw     im
+        Imager::IO     ig
+
+
+#endif /* HAVE_LIBTIFF */
+
+
+
+
+
+#ifdef HAVE_LIBPNG
+
+Imager::ImgRaw
+i_readpng(fd)
+              int     fd
+
+
+undef_int
+i_writepng(im,fd)
+    Imager::ImgRaw     im
+              int     fd
+
+
+Imager::ImgRaw
+i_readpng_scalar(...)
+          PROTOTYPE: $
+            PREINIT:
+             char*    data;
+      unsigned int     length;
+              CODE:
+              data = (char *)SvPV(ST(0), length);
+             RETVAL=i_readpng_scalar(data,length);
+             OUTPUT:
+              RETVAL
+       
+
+
+#endif
+
+
+#ifdef HAVE_LIBGIF
+
+undef_int
+i_writegif(im,fd,colors,pixdev,fixed)
+    Imager::ImgRaw     im
+              int     fd
+              int     colors
+               int     pixdev
+            PREINIT:
+             int     fixedlen;
+            Imager__Color  fixed;
+            Imager__Color  tmp;
+            AV* av;
+            SV* sv1;
+             IV  Itmp;
+            int i;
+            CODE:
+            if (!SvROK(ST(4))) croak("Imager: Parameter 4 must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(4))) != SVt_PVAV) croak("Imager: Parameter 4 must be a reference to an array\n");
+            av=(AV*)SvRV(ST(4));
+            fixedlen=av_len(av)+1;
+            fixed=mymalloc( fixedlen*sizeof(i_color) );
+            for(i=0;i<fixedlen;i++) {
+              sv1=(*(av_fetch(av,i,0)));
+               if (sv_derived_from(sv1, "Imager::Color")) {
+                 Itmp = SvIV((SV*)SvRV(sv1));
+                 tmp = (i_color*) Itmp;
+               } else croak("Imager: one of the elements of array ref is not of Imager::Color type\n");
+               fixed[i]=*tmp;
+            }
+            RETVAL=i_writegif(im,fd,colors,pixdev,fixedlen,fixed);
+             myfree(fixed);
+             ST(0) = sv_newmortal();
+             if (RETVAL == 0) ST(0)=&PL_sv_undef;
+             else sv_setiv(ST(0), (IV)RETVAL);
+
+
+
+
+undef_int
+i_writegifmc(im,fd,colors)
+    Imager::ImgRaw     im
+              int     fd
+              int     colors
+
+undef_int
+i_writegifex(im,fd)
+    Imager::ImgRaw     im
+              int     fd
+
+undef_int
+i_writegif_gen(fd, ...)
+              int     fd
+      PROTOTYPE: $$@
+      PREINIT:
+       i_quantize quant;
+       i_gif_opts opts;
+       i_img **imgs = NULL;
+       int img_count;
+       int i;
+       HV *hv;
+      CODE:
+       if (items < 3)
+           croak("Usage: i_writegif_gen(fd,hashref, images...)");
+       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
+           croak("i_writegif_gen: Second argument must be a hash ref");
+       hv = (HV *)SvRV(ST(1));
+       memset(&quant, 0, sizeof(quant));
+       quant.mc_size = 256;
+       quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
+       memset(&opts, 0, sizeof(opts));
+       handle_quant_opts(&quant, hv);
+       handle_gif_opts(&opts, hv);
+       img_count = items - 2;
+       RETVAL = 1;
+       if (img_count < 1) {
+         RETVAL = 0;
+       }
+       else {
+          imgs = mymalloc(sizeof(i_img *) * img_count);
+          for (i = 0; i < img_count; ++i) {
+           SV *sv = ST(2+i);
+           imgs[i] = NULL;
+           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
+             imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+           }
+           else {
+             RETVAL = 0;
+             break;
+            }
+         }
+          if (RETVAL) {
+           RETVAL = i_writegif_gen(&quant, fd, imgs, img_count, &opts);
+          }
+         myfree(imgs);
+          if (RETVAL) {
+           copy_colors_back(hv, &quant);
+          }
+       }
+             ST(0) = sv_newmortal();
+             if (RETVAL == 0) ST(0)=&PL_sv_undef;
+             else sv_setiv(ST(0), (IV)RETVAL);
+
+undef_int
+i_writegif_callback(cb, maxbuffer,...)
+       int maxbuffer;
+      PREINIT:
+       i_quantize quant;
+       i_gif_opts opts;
+       i_img **imgs = NULL;
+       int img_count;
+       int i;
+       HV *hv;
+        i_writer_data wd;
+      CODE:
+       if (items < 4)
+           croak("Usage: i_writegif_callback(\\&callback,maxbuffer,hashref, images...)");
+       if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
+           croak("i_writegif_callback: Second argument must be a hash ref");
+       hv = (HV *)SvRV(ST(2));
+       memset(&quant, 0, sizeof(quant));
+       quant.mc_size = 256;
+       quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
+       memset(&opts, 0, sizeof(opts));
+       handle_quant_opts(&quant, hv);
+       handle_gif_opts(&opts, hv);
+       img_count = items - 3;
+       RETVAL = 1;
+       if (img_count < 1) {
+         RETVAL = 0;
+       }
+       else {
+          imgs = mymalloc(sizeof(i_img *) * img_count);
+          for (i = 0; i < img_count; ++i) {
+           SV *sv = ST(3+i);
+           imgs[i] = NULL;
+           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
+             imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+           }
+           else {
+             RETVAL = 0;
+             break;
+            }
+         }
+          if (RETVAL) {
+           wd.sv = ST(0);
+           RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count, &opts);
+          }
+         myfree(imgs);
+          if (RETVAL) {
+           copy_colors_back(hv, &quant);
+          }
+       }
+             ST(0) = sv_newmortal();
+             if (RETVAL == 0) ST(0)=&PL_sv_undef;
+             else sv_setiv(ST(0), (IV)RETVAL);
+
+void
+i_readgif(fd)
+              int     fd
+             PREINIT:
+               int*    colour_table;
+               int     colours, q, w;
+             i_img*    rimg;
+                 SV*    temp[3];
+                 AV*    ct; 
+                 SV*    r;
+              PPCODE:
+              colour_table = NULL;
+               colours = 0;
+
+       if(GIMME_V == G_ARRAY) {  
+            rimg = i_readgif(fd,&colour_table,&colours);
+        } else {
+            /* don't waste time with colours if they aren't wanted */
+            rimg = i_readgif(fd,NULL,NULL);
+        }
+
+       if (colour_table == NULL) {
+            EXTEND(SP,1);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+       } else {
+            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
+            /* I don't know if I have the reference counts right or not :( */
+            /* Neither do I :-) */
+            /* No Idea here either */
+
+            ct=newAV();
+            av_extend(ct, colours);
+            for(q=0; q<colours; q++) {
+                for(w=0; w<3; w++)
+                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
+                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
+            }
+            myfree(colour_table);
+            
+            EXTEND(SP,2);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+            PUSHs(newRV_noinc((SV*)ct));
+        }
+
+
+
+
+
+void
+i_readgif_scalar(...)
+          PROTOTYPE: $
+            PREINIT:
+               char*    data;
+       unsigned int     length;
+               int*    colour_table;
+               int     colours, q, w;
+             i_img*    rimg;
+                 SV*    temp[3];
+                 AV*    ct; 
+                 SV*    r;
+              PPCODE:
+        data = (char *)SvPV(ST(0), length);
+        colour_table=NULL;
+        colours=0;
+
+       if(GIMME_V == G_ARRAY) {  
+            rimg=i_readgif_scalar(data,length,&colour_table,&colours);
+        } else {
+            /* don't waste time with colours if they aren't wanted */
+            rimg=i_readgif_scalar(data,length,NULL,NULL);
+        }
+
+       if (colour_table == NULL) {
+            EXTEND(SP,1);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+       } else {
+            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
+            /* I don't know if I have the reference counts right or not :( */
+            /* Neither do I :-) */
+            ct=newAV();
+            av_extend(ct, colours);
+            for(q=0; q<colours; q++) {
+                for(w=0; w<3; w++)
+                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
+                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
+            }
+            myfree(colour_table);
+            
+            EXTEND(SP,2);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+            PUSHs(newRV_noinc((SV*)ct));
+        }
+
+void
+i_readgif_callback(...)
+          PROTOTYPE: &
+            PREINIT:
+               char*    data;
+               int     length;
+               int*    colour_table;
+               int     colours, q, w;
+             i_img*    rimg;
+                 SV*    temp[3];
+                 AV*    ct; 
+                 SV*    r;
+       i_reader_data    rd;
+              PPCODE:
+       rd.sv = ST(0);
+        colour_table=NULL;
+        colours=0;
+
+       if(GIMME_V == G_ARRAY) {  
+            rimg=i_readgif_callback(read_callback, (char *)&rd,&colour_table,&colours);
+        } else {
+            /* don't waste time with colours if they aren't wanted */
+            rimg=i_readgif_callback(read_callback, (char *)&rd,NULL,NULL);
+        }
+
+       if (colour_table == NULL) {
+            EXTEND(SP,1);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+       } else {
+            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
+            /* I don't know if I have the reference counts right or not :( */
+            /* Neither do I :-) */
+            /* Neither do I - maybe I'll move this somewhere */
+            ct=newAV();
+            av_extend(ct, colours);
+            for(q=0; q<colours; q++) {
+                for(w=0; w<3; w++)
+                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
+                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
+            }
+            myfree(colour_table);
+            
+            EXTEND(SP,2);
+            r=sv_newmortal();
+            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
+            PUSHs(r);
+            PUSHs(newRV_noinc((SV*)ct));
+        }
+
+
+
+
+
+
+#endif
+
+
+
+Imager::ImgRaw
+i_readpnm_wiol(ig, length)
+        Imager::IO     ig
+              int     length
+
+
+undef_int
+i_writeppm(im,fd)
+    Imager::ImgRaw     im
+              int     fd
+
+Imager::ImgRaw
+i_readraw(fd,x,y,datachannels,storechannels,intrl)
+              int     fd
+              int     x
+              int     y
+              int     datachannels
+              int     storechannels
+              int     intrl
+
+undef_int
+i_writeraw(im,fd)
+    Imager::ImgRaw     im
+              int     fd
+
+
+Imager::ImgRaw
+i_scaleaxis(im,Value,Axis)
+    Imager::ImgRaw     im
+             float     Value
+              int     Axis
+
+Imager::ImgRaw
+i_scale_nn(im,scx,scy)
+    Imager::ImgRaw     im
+             float     scx
+             float     scy
+
+Imager::ImgRaw
+i_haar(im)
+    Imager::ImgRaw     im
+
+int
+i_count_colors(im,maxc)
+    Imager::ImgRaw     im
+               int     maxc
+
+
+Imager::ImgRaw
+i_transform(im,opx,opy,parm)
+    Imager::ImgRaw     im
+             PREINIT:
+             double* parm;
+             int*    opx;
+             int*    opy;
+             int     opxl;
+             int     opyl;
+             int     parmlen;
+             AV* av;
+             SV* sv1;
+             int i;
+             CODE:
+             if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n");
+             if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to an array\n");
+             if (!SvROK(ST(3))) croak("Imager: Parameter 3 must be a reference to an array\n");
+             if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 must be a reference to an array\n");
+             if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 2 must be a reference to an array\n");
+             if (SvTYPE(SvRV(ST(3))) != SVt_PVAV) croak("Imager: Parameter 3 must be a reference to an array\n");
+             av=(AV*)SvRV(ST(1));
+             opxl=av_len(av)+1;
+             opx=mymalloc( opxl*sizeof(int) );
+             for(i=0;i<opxl;i++) {
+               sv1=(*(av_fetch(av,i,0)));
+               opx[i]=(int)SvIV(sv1);
+             }
+             av=(AV*)SvRV(ST(2));
+             opyl=av_len(av)+1;
+             opy=mymalloc( opyl*sizeof(int) );
+             for(i=0;i<opyl;i++) {
+               sv1=(*(av_fetch(av,i,0)));
+               opy[i]=(int)SvIV(sv1);
+             }
+             av=(AV*)SvRV(ST(3));
+             parmlen=av_len(av)+1;
+             parm=mymalloc( parmlen*sizeof(double) );
+             for(i=0;i<parmlen;i++) { /* FIXME: Bug? */
+               sv1=(*(av_fetch(av,i,0)));
+               parm[i]=(double)SvNV(sv1);
+             }
+             RETVAL=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
+             myfree(parm);
+             myfree(opy);
+             myfree(opx);
+             ST(0) = sv_newmortal();
+             if (RETVAL == 0) ST(0)=&PL_sv_undef;
+             else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+
+Imager::ImgRaw
+i_transform2(width,height,ops,n_regs,c_regs,in_imgs)
+            PREINIT:
+             int width;
+             int height;
+            double* parm;
+            struct rm_op *ops;
+            unsigned int ops_len;
+            int ops_count;
+             double *n_regs;
+             int n_regs_count;
+             i_color *c_regs;
+            int c_regs_count;
+             int in_imgs_count;
+             i_img **in_imgs;
+            AV* av;
+            SV* sv1;
+             IV tmp;
+            int i;
+             CODE:
+            if (!SvROK(ST(3))) croak("Imager: Parameter 4 must be a reference to an array\n");
+            if (!SvROK(ST(4))) croak("Imager: Parameter 5 must be a reference to an array\n");
+            if (!SvROK(ST(5))) croak("Imager: Parameter 6 must be a reference to an array of images\n");
+            if (SvTYPE(SvRV(ST(3))) != SVt_PVAV) croak("Imager: Parameter 4 must be a reference to an array\n");
+            if (SvTYPE(SvRV(ST(4))) != SVt_PVAV) croak("Imager: Parameter 5 must be a reference to an array\n");
+
+       /*if (SvTYPE(SvRV(ST(5))) != SVt_PVAV) croak("Imager: Parameter 6 must be a reference to an array\n");*/
+
+             if (SvTYPE(SvRV(ST(5))) == SVt_PVAV) {
+              av = (AV*)SvRV(ST(5));
+               in_imgs_count = av_len(av)+1;
+              for (i = 0; i < in_imgs_count; ++i) {
+                sv1 = *av_fetch(av, i, 0);
+                if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
+                  croak("Parameter 5 must contain only images");
+                }
+              }
+            }
+            else {
+              in_imgs_count = 0;
+             }
+             if (SvTYPE(SvRV(ST(5))) == SVt_PVAV) {
+               av = (AV*)SvRV(ST(5));
+               in_imgs = mymalloc(in_imgs_count*sizeof(i_img*));
+               for (i = 0; i < in_imgs_count; ++i) {              
+                sv1 = *av_fetch(av,i,0);
+                if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
+                  croak("Parameter 5 must contain only images");
+                }
+                 tmp = SvIV((SV*)SvRV(sv1));
+                in_imgs[i] = (i_img*)tmp;
+              }
+            }
+             else {
+              /* no input images */
+              in_imgs = NULL;
+             }
+             /* default the output size from the first input if possible */
+             if (SvOK(ST(0)))
+              width = SvIV(ST(0));
+             else if (in_imgs_count)
+              width = in_imgs[0]->xsize;
+             else
+              croak("No output image width supplied");
+
+             if (SvOK(ST(1)))
+              height = SvIV(ST(1));
+             else if (in_imgs_count)
+              height = in_imgs[0]->ysize;
+             else
+              croak("No output image height supplied");
+
+            ops = (struct rm_op *)SvPV(ST(2), ops_len);
+             if (ops_len % sizeof(struct rm_op))
+                croak("Imager: Parameter 3 must be a bitmap of regops\n");
+            ops_count = ops_len / sizeof(struct rm_op);
+            av = (AV*)SvRV(ST(3));
+            n_regs_count = av_len(av)+1;
+             n_regs = mymalloc(n_regs_count * sizeof(double));
+            for (i = 0; i < n_regs_count; ++i) {
+              sv1 = *av_fetch(av,i,0);
+              if (SvOK(sv1))
+                n_regs[i] = SvNV(sv1);
+            }
+             av = (AV*)SvRV(ST(4));
+             c_regs_count = av_len(av)+1;
+             c_regs = mymalloc(c_regs_count * sizeof(i_color));
+             /* I don't bother initializing the colou?r registers */
+
+            RETVAL=i_transform2(width, height, 3, ops, ops_count, 
+                                n_regs, n_regs_count, 
+                                c_regs, c_regs_count, in_imgs, in_imgs_count);
+            if (in_imgs)
+                myfree(in_imgs);
+             myfree(n_regs);
+            myfree(c_regs);
+             ST(0) = sv_newmortal();
+             if (RETVAL == 0) ST(0)=&PL_sv_undef;
+             else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+
+
+void
+i_contrast(im,intensity)
+    Imager::ImgRaw     im
+             float     intensity
+
+void
+i_hardinvert(im)
+    Imager::ImgRaw     im
+
+void
+i_noise(im,amount,type)
+    Imager::ImgRaw     im
+             float     amount
+     unsigned char     type
+
+void
+i_bumpmap(im,bump,channel,light_x,light_y,strength)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     bump
+               int     channel
+               int     light_x
+               int     light_y
+               int     strength
+
+void
+i_postlevels(im,levels)
+    Imager::ImgRaw     im
+             int       levels
+
+void
+i_mosaic(im,size)
+    Imager::ImgRaw     im
+               int     size
+
+void
+i_watermark(im,wmark,tx,ty,pixdiff)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     wmark
+               int     tx
+               int     ty
+               int     pixdiff
+
+
+void
+i_autolevels(im,lsat,usat,skew)
+    Imager::ImgRaw     im
+             float     lsat
+             float     usat
+             float     skew
+
+void
+i_radnoise(im,xo,yo,rscale,ascale)
+    Imager::ImgRaw     im
+             float     xo
+             float     yo
+             float     rscale
+             float     ascale
+
+void
+i_turbnoise(im, xo, yo, scale)
+    Imager::ImgRaw     im
+             float     xo
+             float     yo
+             float     scale
+
+
+void
+i_gradgen(im, ...)
+    Imager::ImgRaw     im
+      PREINIT:
+       int num;
+       int *xo;
+       int *yo;
+        i_color *ival;
+       int dmeasure;
+       int i;
+       SV *sv;
+       AV *axx;
+       AV *ayy;
+       AV *ac;
+      CODE:
+       if (items != 5)
+           croak("Usage: i_gradgen(im, xo, yo, ival, dmeasure)");
+       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
+           croak("i_gradgen: Second argument must be an array ref");
+       if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
+           croak("i_gradgen: Third argument must be an array ref");
+       if (!SvROK(ST(3)) || ! SvTYPE(SvRV(ST(3))))
+           croak("i_gradgen: Fourth argument must be an array ref");
+       axx = (AV *)SvRV(ST(1));
+       ayy = (AV *)SvRV(ST(2));
+       ac  = (AV *)SvRV(ST(3));
+       dmeasure = (int)SvIV(ST(4));
+       
+        num = av_len(axx) < av_len(ayy) ? av_len(axx) : av_len(ayy);
+       num = num <= av_len(ac) ? num : av_len(ac);
+       num++; 
+       if (num < 2) croak("Usage: i_gradgen array refs must have more than 1 entry each");
+       xo = mymalloc( sizeof(int) * num );
+       yo = mymalloc( sizeof(int) * num );
+       ival = mymalloc( sizeof(i_color) * num );
+       for(i = 0; i<num; i++) {
+         xo[i]   = (int)SvIV(* av_fetch(axx, i, 0));
+         yo[i]   = (int)SvIV(* av_fetch(ayy, i, 0));
+          sv = *av_fetch(ac, i, 0);
+         if ( !sv_derived_from(sv, "Imager::Color") ) {
+           free(axx); free(ayy); free(ac);
+            croak("i_gradgen: Element of fourth argument is not derived from Imager::Color");
+         }
+         ival[i] = *(i_color *)SvIV((SV *)SvRV(sv));
+       }
+        i_gradgen(im, num, xo, yo, ival, dmeasure);
+
+
+
+
+
+
+
+
+void
+i_nearest_color(im, ...)
+    Imager::ImgRaw     im
+      PREINIT:
+       int num;
+       int *xo;
+       int *yo;
+        i_color *ival;
+       int dmeasure;
+       int i;
+       SV *sv;
+       AV *axx;
+       AV *ayy;
+       AV *ac;
+      CODE:
+       if (items != 5)
+           croak("Usage: i_nearest_color(im, xo, yo, ival, dmeasure)");
+       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
+           croak("i_nearest_color: Second argument must be an array ref");
+       if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
+           croak("i_nearest_color: Third argument must be an array ref");
+       if (!SvROK(ST(3)) || ! SvTYPE(SvRV(ST(3))))
+           croak("i_nearest_color: Fourth argument must be an array ref");
+       axx = (AV *)SvRV(ST(1));
+       ayy = (AV *)SvRV(ST(2));
+       ac  = (AV *)SvRV(ST(3));
+       dmeasure = (int)SvIV(ST(4));
+       
+        num = av_len(axx) < av_len(ayy) ? av_len(axx) : av_len(ayy);
+       num = num <= av_len(ac) ? num : av_len(ac);
+       num++; 
+       if (num < 2) croak("Usage: i_nearest_color array refs must have more than 1 entry each");
+       xo = mymalloc( sizeof(int) * num );
+       yo = mymalloc( sizeof(int) * num );
+       ival = mymalloc( sizeof(i_color) * num );
+       for(i = 0; i<num; i++) {
+         xo[i]   = (int)SvIV(* av_fetch(axx, i, 0));
+         yo[i]   = (int)SvIV(* av_fetch(ayy, i, 0));
+          sv = *av_fetch(ac, i, 0);
+         if ( !sv_derived_from(sv, "Imager::Color") ) {
+           free(axx); free(ayy); free(ac);
+            croak("i_nearest_color: Element of fourth argument is not derived from Imager::Color");
+         }
+         ival[i] = *(i_color *)SvIV((SV *)SvRV(sv));
+       }
+        i_nearest_color(im, num, xo, yo, ival, dmeasure);
+
+
+
+
+void
+malloc_state()
+
+void
+hashinfo(hv)
+            PREINIT:
+              HV* hv;
+              int stuff;
+            PPCODE:
+              if (!SvROK(ST(0))) croak("Imager: Parameter 0 must be a reference to a hash\n");        
+              hv=(HV*)SvRV(ST(0));
+              if (SvTYPE(hv)!=SVt_PVHV) croak("Imager: Parameter 0 must be a reference to a hash\n");
+              if (getint(hv,"stuff",&stuff)) printf("ok: %d\n",stuff); else printf("key doesn't exist\n");
+              if (getint(hv,"stuff2",&stuff)) printf("ok: %d\n",stuff); else printf("key doesn't exist\n");
+              
+void
+DSO_open(filename)
+             char*       filename
+            PREINIT:
+              void *rc;
+              char *evstr;
+            PPCODE:
+              rc=DSO_open(filename,&evstr);
+               if (rc!=NULL) {
+                 if (evstr!=NULL) {
+                   EXTEND(SP,2); 
+                   PUSHs(sv_2mortal(newSViv((IV)rc)));
+                   PUSHs(sv_2mortal(newSVpvn(evstr, strlen(evstr))));
+                 } else {
+                   EXTEND(SP,1);
+                   PUSHs(sv_2mortal(newSViv((IV)rc)));
+                 }
+               }
+
+
+undef_int
+DSO_close(dso_handle)
+             void*       dso_handle
+
+void
+DSO_funclist(dso_handle_v)
+             void*       dso_handle_v
+            PREINIT:
+              int i;
+              DSO_handle *dso_handle;
+            PPCODE:
+              dso_handle=(DSO_handle*)dso_handle_v;
+              i=0;
+              while( dso_handle->function_list[i].name != NULL) {
+                EXTEND(SP,1);
+                PUSHs(sv_2mortal(newSVpv(dso_handle->function_list[i].name,0)));
+                EXTEND(SP,1);
+                PUSHs(sv_2mortal(newSVpv(dso_handle->function_list[i++].pcode,0)));
+              }
+
+
+void
+DSO_call(handle,func_index,hv)
+              void*  handle
+              int    func_index
+            PREINIT:
+              HV* hv;
+            PPCODE:
+              if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to a hash\n");        
+              hv=(HV*)SvRV(ST(2));
+              if (SvTYPE(hv)!=SVt_PVHV) croak("Imager: Parameter 2 must be a reference to a hash\n");
+              DSO_call( (DSO_handle *)handle,func_index,hv);
+
+
+
+
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..e6bb8e5
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,95 @@
+Changes
+Imager.pm
+Imager.xs
+MANIFEST
+README
+Makefile.PL
+draw.c
+draw.h
+conv.c
+gaussian.c
+ppport.h
+image.c
+image.h
+datatypes.h
+datatypes.c
+feat.h
+io.c
+io.h
+log.c
+log.h
+jpeg.c
+png.c
+gif.c
+tiff.c
+quant.c
+raw.c
+pnm.c
+filters.c
+feat.c
+font.c
+regmach.c
+regmach.h
+stackmach.c
+stackmach.h
+trans2.c
+iolayer.h
+iolayer.c
+fontfiles/dcr10.afm
+fontfiles/dcr10.pfb
+fontfiles/dodge.ttf
+lib/Imager/regmach.pod
+lib/Imager/Regops.pm
+lib/Imager/Expr.pm
+lib/Imager/Expr/Assem.pm
+lib/Imager/Color.pm
+lib/Imager/Font.pm
+lib/Imager/Transform.pm
+t/t00basic.t
+t/t10formats.t
+t/t15color.t
+t/t30t1font.t
+t/t35ttfont.t
+t/t36oofont.t
+t/t40scale.t
+t/t50basicoo.t
+t/t55trans.t
+t/t56postfix.t
+t/t57infix.t
+t/t58trans2.t
+t/t59assem.t
+t/t60dyntest.t
+t/t65crop.t
+t/t66paste.t
+t/t70newgif.t
+t/t75polyaa.t
+t/t90cc.t
+testimg/scale.ppm
+testimg/scale.gif
+testimg/scalei.gif
+testimg/penguin-base.ppm
+typemap
+dynaload.c
+dynaload.h
+ext.h
+plug.h
+filterlist.perl
+doco.perl
+errep.perl
+regops.perl
+spot.perl      For making an ordered dither matrix from a spot function
+transbench.perl
+transform.perl Shell interface to Imager::Transform
+bigtest.perl   Library selection tester
+dynfilt/pluginst.h
+dynfilt/dyntest.c
+dynfilt/dt2.c
+dynfilt/mandelbrot.c
+dynfilt/flines.c
+dynfilt/compile.txt
+dynfilt/Makefile.PL
+bench/quantone.perl    One round of benchmarking image quantization.
+bench/quantbench.perl  Benchmark various quantization methods
+bench/makegrad.perl    Builds regular images for quantization.
+bench/kscdisplay.png   Photo test for quantization.
+bench/benchform.perl   Formats the benchmark results
\ No newline at end of file
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..ce577a1
--- /dev/null
@@ -0,0 +1,371 @@
+
+use ExtUtils::MakeMaker;
+use Cwd;
+use Config;
+
+$lext=$Config{'so'};   # Get extensions of libraries
+
+#
+# IM_INCPATH      colon seperated list of paths to extra include paths
+# IM_LIBPATH      colon seperated list of paths to extra library paths
+#
+# IM_VERBOSE      turns on verbose mode for the library finding and such
+# IM_MANUAL       to manually select which libraries are used and which not
+# IM_ENABLE       to programmatically select which libraries are used
+#                 and which are not
+# IM_NOLOG        if true logging will not be compiled into the module
+# IM_DEBUG_MALLOC if true malloc debbuging will be compiled into the module
+#                 do not use IM_DEBUG_MALLOC in production - this slows
+#                 everything down by alot
+# IM_CFLAGS       Extra flags to pass to the compiler
+# IM_LFLAGS       Extra flags to pass to the linker
+# IM_DFLAGS       Extra flags to pass to the preprocessor
+
+
+getenv();     # get environment variables
+init();       # initialize global data
+pathcheck();  # Check if directories exist
+
+# Pick what libraries are used
+if ($MANUAL) {
+  manual();
+} else {
+  automatic();
+  if (exists $ENV{IM_ENABLE}) {
+    my %en = map { $_, 1 } split ' ', $ENV{IM_ENABLE};
+    for my $key (keys %formats) {
+      delete $formats{$key} unless $en{$key};
+    }
+  }
+}
+
+# Make sure there isn't a clash between the gif libraries.
+gifcheck();
+
+for $frm(values %formats) {
+  $F_DEFINE .= ' -D'.$frm->{def};
+  $F_LIBS   .= ' '  .$frm->{libfiles};
+  $F_OBJECT .= ' '  .$frm->{objfiles};
+}
+
+$F_INC  = join(" ",map { (exists $definc{$_})?'':'-I'.$_ } @incs);
+$F_LIBS = join(" ",map { '-L'.$_ } @libs).' '.$F_LIBS;
+
+$OSLIBS = '';
+$OSDEF  = "-DOS_$^O";
+
+if ($^O eq 'hpux')                { $OSLIBS .= ' -ldld'; }
+if (defined $Config{'d_dlsymun'}) { $OSDEF  .= ' -DDLSYMUN'; }
+
+@objs = qw(Imager.o draw.o image.o io.o iolayer.o log.o
+          gaussian.o conv.o pnm.o raw.o feat.o font.o
+          filters.o dynaload.o stackmach.o datatypes.o
+          regmach.o trans2.o quant.o);
+
+%opts=(
+       'NAME'         => 'Imager',
+       'VERSION_FROM' => 'Imager.pm',
+       'LIBS'         => "$LFLAGS -lm $OSLIBS $F_LIBS",
+       'DEFINE'       => "$F_DEFINE $EXTDEF $OSDEF $CFLAGS",
+       'INC'          => "$DFLAGS $F_INC",
+       'OBJECT'       => join(' ', @objs, $F_OBJECT)
+      );
+
+if ($VERBOSE) { print Dumper(\%opts); }
+mkdir('testout',0777); # since we cannot include it in the archive.
+WriteMakefile(%opts);
+exit;
+
+
+sub MY::postamble {
+'
+dyntest.(MYEXTLIB) : dynfilt/Makefile
+       cd dynfilt && $(MAKE) $(PASTHRU)
+
+lib/Imager/Regops.pm : regmach.h regops.perl
+       $(PERL) regops.perl regmach.h lib/Imager/Regops.pm
+';
+}
+
+# manual configuration of helper libraries
+
+sub manual {
+  print <<EOF;
+
+      Please answer the following questions about
+      which formats are avaliable on your computer
+
+press <return> to continue
+EOF
+
+  <STDIN>; # eat one return
+
+  for $frm(sort { $formats{$b}{order} <=> $formats{$a}{order} } keys %formats) {
+  SWX:
+    if ($formats{$frm}{docs}) { print "\n",$formats{$frm}{docs},"\n\n"; }
+    print "Enable $frm support: ";
+    $gz=<STDIN>;
+    chomp($gz);
+    if ($gz =~ m/^(y|yes|n|no)/i) {
+      $gz=substr(lc($gz),0,1);
+      if ($gz eq 'n') {
+       delete $formats{$frm};
+      }
+    } else { goto SWX; }
+  }
+}
+
+
+# automatic configuration of helper libraries
+
+sub automatic {
+  for $frm(keys %formats) {
+    delete $formats{$frm} if !checkformat($frm);       
+  }
+}
+
+
+sub gifcheck {
+  if ($formats{'gif'} and $formats{'ungif'}) { 
+    print "ungif and gif can not coexist - removing ungif support\n";
+    delete $formats{'ungif'};
+  }
+  my @dirs;
+  for my $frm (grep $formats{$_}, qw(gif ungif)) {
+    push(@dirs, @{$formats{$frm}{incdir}}) if $formats{$frm}{incdir};
+  }
+  my $minor = 0;
+  my $major = 0;
+  FILES: for my $dir (@dirs) {
+    my $h = "$dir/gif_lib.h";
+    open H, "< $h" or next;
+    while (<H>) {
+      if (/GIF_LIB_VERSION\s+"\s*version\s*(\d+)\.(\d+)/i) {
+       $major = $1;
+       $minor = $2;
+       close H;
+       last FILES;
+      }
+    }
+    close H;
+  }
+
+  # we need the version in a #ifdefable form
+  
+  $F_DEFINE .= "-DIM_GIFMAJOR=$major -DIM_GIFMINOR=$minor";
+}
+
+
+sub gd {
+  my($path,$chk)=@_;
+
+#    print "checking path $path\n";
+  if ( !opendir(DH,$path) ) {
+    warn "Cannot open dir $path: $!\n";
+    return;
+  }
+  my @l=grep { $chk->($_) } readdir(DH);
+  #    print @l;
+  close(DH);
+  return map $path, @l;
+}
+
+
+sub checkformat {
+  my $frm=shift;
+  my $libchk=$formats{$frm}{'libcheck'};
+  my $incchk=$formats{$frm}{'inccheck'};
+
+  my @l;
+  for my $lp (@libs) {
+    push(@l, gd($lp,$libchk));
+  }
+
+  my @i;
+  for my $ip (@incs) {
+    push(@i, gd($ip,$incchk));
+  }
+
+  printf("%10s: includes %s - libraries %s\n",$frm,(@i?'found':'not found'),(@l?'found':'not found'));
+  $formats{$frm}{incdir} = \@i;
+  $formats{$frm}{libdir} = \@l;
+  return scalar(@i && @l);
+}
+
+
+
+
+sub pathcheck {
+  if ($VERBOSE) {
+    print "pathcheck\n";
+    print "  Include paths:\n";
+    for (@incs) { print $_,"\n"; }
+  }
+  @incs=grep { -d $_ && -r _ && -x _ or ( print("  $_ doesnt exist or is unaccessible - removed."),0) } @incs;
+
+  if ($VERBOSE) {
+    print "\nLibrary paths:\n";
+    for (@incs) { print $_,"\n"; }
+  }
+  @libs=grep { -d $_ && -r _ && -x _ or ( print("  $_ doesnt exist or is unaccessible - removed."),0) } @libs;
+  print "\ndone.\n";
+}
+
+
+# Format data initialization
+
+# format definition is:
+# defines needed
+# default include path
+# files needed for include (boolean perl code)
+# default lib path
+# libs needed
+# files needed for link (boolean perl code)
+# object files needed for the format
+
+
+sub init {
+
+  @definc{'/usr/include'}=();
+  @incs=(qw(/usr/include /usr/local/include /usr/include/freetype /usr/local/include/freetype), split /:/, $INCPATH );
+  @libs=(split(/ /, $Config{'libpth'}), split(/:/, $LIBPATH) );
+
+  $formats{'jpeg'}={
+                   order=>'21',
+                   def=>'HAVE_LIBJPEG',
+                   inccheck=>sub { $_[0] eq 'jpeglib.h' },
+                   libcheck=>sub { $_[0] eq 'libjpeg.a' or $_ eq "libjpeg.$lext" },
+                   libfiles=>'-ljpeg',
+                   objfiles=>'jpeg.o',
+                   docs=>q{
+                           In order to use jpeg with this module you need to have libjpeg
+                           installed on your computer}
+                  };
+
+  $formats{'tiff'}={
+                   order=>'23',
+                   def=>'HAVE_LIBTIFF',
+                   inccheck=>sub { $_[0] eq 'tiffio.h' },
+                   libcheck=>sub { $_[0] eq 'libtiff.a' or $_ eq "libtiff.$lext" },
+                   libfiles=>'-ltiff',
+                   objfiles=>'tiff.o',
+                   docs=>q{
+                           In order to use tiff with this module you need to have libtiff
+                           installed on your computer}
+                  };
+
+  $formats{'png'}={
+                  order=>'22',
+                  def=>'HAVE_LIBPNG',
+                  inccheck=>sub { $_[0] eq 'png.h' },
+                  libcheck=>sub { $_[0] eq 'libpng.a' or $_[0] eq "libpng.$lext" },
+                  libfiles=>'-lpng -lz',
+                  objfiles=>'png.o',
+                  docs=>q{
+                          Png stands for Portable Network Graphics and is intended as
+                          a replacement for gif on the web. It is patent free and
+                          is recommended by the w3c, you need libpng to use these formats}
+                 };
+
+  $formats{'gif'}={
+                  order=>'20',
+                  def=>'HAVE_LIBGIF',
+                  inccheck=>sub { $_[0] eq 'gif_lib.h' },
+                  libcheck=>sub { $_[0] eq 'libgif.a' or $_[0] eq "libgif.$lext" },
+                  libfiles=>'-lgif',
+                  objfiles=>'gif.o',
+                  docs=>q{
+                          gif is the de facto standard for webgraphics at the moment,
+                          it does have some patent problems. If you have giflib and
+                          are not in violation with the unisys patent you should use
+                          this instead of the 'ungif' option.  Note that they cannot
+                          be in use at the same time}
+                 };
+
+  $formats{'ungif'}={
+                    order=>'21',
+                    def=>'HAVE_LIBGIF',
+                    inccheck=>sub { $_[0] eq 'gif_lib.h' },
+                    libcheck=>sub { $_[0] eq 'libungif.a' or $_[0] eq "libungif.$lext" },
+                    libfiles=>'-lungif',
+                    objfiles=>'gif.o',
+                    docs=>q{
+                            gif is the de facto standard for webgraphics at the moment,
+                            it does have some patent problems. If you have libungif and
+                            want to create images free from LZW patented compression you
+                            should use this option instead of the 'gif' option}
+                   };
+
+  $formats{'T1-fonts'}={
+                       order=>'30',
+                       def=>'HAVE_LIBT1',
+                       inccheck=>sub { $_[0] eq 't1lib.h' },
+                       libcheck=>sub { $_[0] eq 'libt1.a' or $_[0] eq "libt1.$lext" },
+                       libfiles=>'-lt1',
+                       objfiles=>'',
+                       docs=>q{
+                               postscript t1 fonts are scalable fonts. They can include 
+                               ligatures and kerning information and generally yield good
+                               visual quality. We depend on libt1 to rasterize the fonts
+                               for use in images.}
+                      };
+
+  $formats{'TT-fonts'}={
+                       order=>'31',
+                       def=>'HAVE_LIBTT',
+                       inccheck=>sub { $_[0] eq 'freetype.h' },
+                       libcheck=>sub { $_[0] eq 'libttf.a' or $_[0] eq "libttf.$lext" },
+                       libfiles=>'-lttf',
+                       objfiles=>'',
+                       docs=>q{
+                               Truetype fonts are scalable fonts. They can include 
+                               kerning and hinting information and generally yield good
+                               visual quality esp on low resultions. The freetype library is
+                               used to rasterize for us. The only drawback is that there
+                               are alot of badly designed fonts out there.}
+                      };
+  # Make fix indent
+  for (keys %formats) { $formats{$_}->{docs} =~ s/^\s+/  /mg; }
+}
+
+
+
+sub gen {
+  my $V = $ENV{$_[0]};
+  defined($V) ? $V : "";
+}
+
+
+# Get information from environment variables
+
+sub getenv {
+
+  ($VERBOSE,
+   $INCPATH,
+   $LIBPATH,
+   $NOLOG,
+   $DEBUG_MALLOC,
+   $MANUAL,
+   $CFLAGS,
+   $LFLAGS,
+   $DFLAGS) = map { gen $_ } qw(IM_VERBOSE
+                               IM_INCPATH
+                               IM_LIBPATH
+                               IM_NOLOG
+                               IM_DEBUG_MALLOC
+                               IM_MANUAL
+                               IM_CFLAGS
+                               IM_LFLAGS
+                               IM_DFLAGS);
+
+  if ($VERBOSE) { print "Verbose mode\n"; require Data::Dumper; import Data::Dumper qw(Dumper);}
+
+  if ($NOLOG)   { print "Logging not compiled into module\n"; }
+  else { $EXTDEF.=' -DIMAGER_LOG'; }
+
+  if ($DEBUG_MALLOC) {
+    $EXTDEF.=' -DIMAGER_DEBUG_MALLOC';
+    print "Malloc debugging enabled\n";
+  }
+
+}
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..07a8e37
--- /dev/null
+++ b/README
@@ -0,0 +1,217 @@
+================================================================
+Copyright (c) 1999, 2000 Arnar M. Hrafnkelsson. All rights reserved.
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+================================================================
+
+>> THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY WHATSOEVER <<
+
+================================================================
+
+
+========================
+1. Patent infringements?
+========================
+
+Imager as such contains no patented algorithms.  The external
+libraries (which are not written by me) may or may not contain
+patented algorithms.  YOU ARE SOLELY RESPONSIBLE FOR OBTAINING
+LICENSE(S) TO USE SUCH LIBRARIES SHOULD YOU NEED ANY.
+
+
+========================
+2. Compiling and testing
+========================
+
+Some care has been taken to make the installation as smooth as
+possible.  This is rather hard due to the difference between operating
+systems and site setups.  To get started just type
+
+$ perl Makefile.PL
+
+It should blurb out a list of which libraries were found and which
+not.  If you add a library to the machine after installing Imager it
+does not automatically become available in Imager.  It only uses the
+libraries that are found.  If the list of found libraries is not what
+you expected, then the Makefile.PL is either not searching in the
+right directories or your box does not have the libraries you think it
+does.  For a list of where to get the libraries have a look at
+3. External dependencies.  To widen the search path for libraries and
+include files set the IM_INCPATH and IM_LIBPATH variables.  The
+environment variables that matter when Makefile.PL is run are
+
+IM_INCPATH      colon separated list of paths to extra include files
+IM_LIBPATH      colon separated list of paths to extra library files
+
+IM_VERBOSE      turns on verbose mode for the library scanning and such
+IM_MANUAL       to manually select which libraries are used and which not
+IM_NOLOG        if true logging will not be compiled into the module
+IM_DEBUG_MALLOC if true malloc debugging will be compiled into the module
+                do not use IM_DEBUG_MALLOC in production - this slows
+                everything down
+
+IM_CFLAGS       Extra flags to pass to the compiler
+IM_LFLAGS       Extra flags to pass to the linker
+IM_DFLAGS       Extra flags to pass to the preprocessor
+
+
+
+When finding the libraries has been sorted out it's time for
+
+$ make 
+
+and if that works then do
+
+$ make test
+
+If either fails do take a peek at the file errep.perl.  It's creates a
+file report.txt.  This is some information which will help me discover
+where the problem is so I can try to fix it in future releases.  If
+you find running it ok (just remember - no warranty!) please send the
+report.txt via email to addi@umich.edu .
+
+Troubleshooting tips:
+
+A common problem is that libgif/libungif are sometimes linked to the X
+libraries and then running the tests fails.  In that case something
+like:
+
+$ IM_LFLAGS="-L/usr/X11R6/lib -lX11" perl Makefile.PL
+
+Which simply sets the environment variables for the extra libraries 
+to include the X libraries (which we do not use at all, but must
+included since libgif has been linked with it).
+
+Also note that libgif has a few bugs: You can run something like
+
+$ perl -Iblib/lib -Iblib/arch t/t10formats.t
+
+This way you can see what comments the test script prints out.
+t/t10formats.t checks for an bug in libgiff and prints out a patch
+if that bug is present, note that this bug only affects the more
+"advanced" features of libgif.
+
+Imager needs to have a fairly recent libtiff installed (we know it
+it runs fine with 3.5.5).  In the future we might consider supporting older
+libtiff versions.  For now you can either configure Imager manually (by
+setting the IM_MANUAL environment variable to 1, in sh:
+
+$ IM_MANUAL=1 perl Makefile.PL
+
+and simply say no to tiff support when asked if you want it, the same thing
+can be used to circumvent problems in gifs to get Imager going.
+
+
+If it worked just continue with the installation as normally 
+(with make install).
+
+========================
+3. External dependencies
+========================
+
+Some hints about getting the Imager module to find the libraries it
+needs for specific features.  The libraries it uses are:
+
+ jpeg: ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
+
+  png: ftp://ftp.uu.net/graphics/png  
+  you also need zlib to use png:  ftp://ftp.uu.net/pub/archiving/zip/zlib
+
+  gif: http://www.arrakeen.demon.co.uk/giflib/  
+or at: http://prtr-13.ucsc.edu/~badger/software/libungif/index.shtml
+
+ tiff: http://www.libtiff.org/
+
+   t1: ftp://ftp.neuroinformatik.ruhr-uni-bochum.de/pub/software/t1lib/
+or at: ftp://sunsite.unc.edu/pub/Linux/libs/graphics/
+
+   tt: http://www.freetype.org/
+
+
+Precompiled versions of some of the libraries might be found at:
+
+AIX:
+   http://www.bull.de/
+   ftp://ftp.rge.com/pub/systems/aix/bull/
+
+
+
+========================
+4. Logging and debugging
+========================
+
+Logging is compiled in by default - if you should want to get of it
+from the binaries you can do so by setting the env IMAGER_NOLOG 
+to something. If you want to enable malloc debugging to check for leaks
+then set IMAGER_DEBUG_MALLOC to something.  Needless to say it is
+pretty pointless to have malloc debug enabled with no logging since you 
+can never see the malloc information that way.
+
+
+========================
+5. Truetype information
+========================
+
+If you enable the truetype part and want it to be TESTED you MUST set
+the environment variable TTFONTTEST to a complete path to a truetype
+font.
+
+
+=================
+6. Win32 Support
+=================
+
+Imager can be installed on Win32 systems.  This was ported and tested
+with Microsoft Visual C++ 6.0 with build 623 of ActivePerl.  Currently
+I haven't tried to test any of the image formats except for ppm, since
+I don't have the required libraries on my NT machine.  This may
+change.  The same is true for font support, though it might be
+preferable to try to use Win32's native font support over the external
+librarie - why force the user to install yet another library.
+
+I haven't tried to target compilers other than VC++, since I don't
+have them installed.
+
+If you have any problems with the Win32 support, please email
+tony@develop-help.com (don't forget to use nmake instead of make).
+
+=======================
+7. General information
+=======================
+
+The Imager module homepage is currently at:
+
+ http://www.eecs.umich.edu/~addi/perl/Imager/
+
+The current docs are rather bad as I've been busy adding features
+but hopefully they will be updated soon.  Until then you'll just
+have to use the source. The test scripts might also be a good idea.
+By activating the the #init_log lines in the test script you can get
+rather verbose debugging output from the C code.
+
+
+========================
+8. Thanks
+========================
+
+Thanks go to:
+  Tony Cook           ( TonyC )
+  Claes Jacobson      ( Claes )
+  Philip Gwyn         ( Leolo )
+  Brad Murray         ( HalfJack )
+  Nicholas Dronen     ( Veblen )
+  Michael G Schwern   ( Schwern )
+  Rocco Caputo        ( Dngor )
+  Graham barr         ( Gbarr )
+  Mark-Jason Dominus  ( Mjd )
+  Jerome
+  Jason Alexander     ( Jalex )
+  Randal R. Schwartz  ( Merlyn )
+  Tkil                ( )
+  Artur Bergman       ( Sky )
+  Luc St-Louis        ( Lucs )
+  PerlJam             ( )
+  Roderick Schertler  ( Roderick )
+  Nathan Torkington   ( gnat )
+
+(and just to play it safe) all those I forgot to mention.
diff --git a/bench/benchform.perl b/bench/benchform.perl
new file mode 100644 (file)
index 0000000..1734cee
--- /dev/null
@@ -0,0 +1,34 @@
+#!perl -w
+use strict;
+<>; #drop the uname
+my @text = <>;
+$text[0] =~ s/\$VAR1/\$data/;
+my $data;
+eval join '', @text;
+print <<EOS;
+                     |       closest      |      errdiff       |
+algorithm |  image   | mono | addi |webmap| mono | addi |webmap|
+EOS
+for my $algo (qw(hashbox sortchan linsearch rand2dist)) {
+  for my $image (qw(rgbtile hsvgrad kscdisplay)) {
+    printf("%-10s|%-10s|", $algo, $image);
+    for my $tran (qw(closest errdiff)) {
+      for my $pal (qw(mono addi webmap)) {
+       printf("%6.2f|", $data->{$algo}{$image}{$tran}{$pal});
+      }
+    }
+    print "\n";
+  }
+}
+
+__END__
+
+=head1 NAME
+
+ benchform.perl - formats quantbench.perl results into a table.
+
+=head1 SYNOPSIS
+
+ perl benchform.perl quantbench.txt
+
+=cut
diff --git a/bench/kscdisplay.png b/bench/kscdisplay.png
new file mode 100644 (file)
index 0000000..b975bb4
Binary files /dev/null and b/bench/kscdisplay.png differ
diff --git a/bench/makegrad.perl b/bench/makegrad.perl
new file mode 100644 (file)
index 0000000..6d90222
--- /dev/null
@@ -0,0 +1,41 @@
+#!perl -w
+use Imager;
+
+=head1 NAME
+
+makegrad.perl - builds a large gradient image for quant.c benchmarking
+
+=head1 SYNOPSIS
+
+  perl makegrad.perl
+
+=cut
+
+# a trans2 script to produce our pretty graduation
+my $hsv = <<'EOS';
+y cy - x cx - atan2 pi / 180 * !hue
+1 1 x cx / y cy / distance !sat
+@hue @sat 1 hsv
+EOS
+
+my $img = Imager::transform2({rpnexpr=>$hsv, width=>600, height=>600})
+  or die "transform2 failed: $Imager::ERRSTR";
+
+$img->write(file=>'hsvgrad.png', type=>'png')
+  or die "Write to hsvgrad.png failed: ", $img->errstr;
+
+# trans2 code to produce RGB tiles
+my $rgb = <<'EOS';
+8 !tilesper
+w @tilesper / !tilex
+h @tilesper / !tiley
+x @tilex % @tilex / 255 * !red
+y @tiley % @tiley / 255 * !green 
+x @tilex / int 
+y @tiley / int @tilesper * + @tilesper @tilesper * / 255 * !blue
+@red @green @blue rgb
+EOS
+$img = Imager::transform2({rpnexpr=>$rgb, width=>600, height=>600})
+  or die "transform2 failed: $Imager::ERRSTR";
+$img->write(file=>'rgbtile.png', type=>'png')
+  or die "write to rgbtile failed: ",$img->errstr;
diff --git a/bench/quantbench.perl b/bench/quantbench.perl
new file mode 100644 (file)
index 0000000..9ce6e28
--- /dev/null
@@ -0,0 +1,105 @@
+#!perl -w
+use strict;
+use Data::Dumper;
+
+print <<EOS;
+This program benchmarks various RGB to palette index code segments (quant.c).
+
+The idea is to run it on various processors to see which is fastest on
+average.
+
+It takes about 50 minutes on my 700MHz PIII laptop.
+
+If you want to see what the output images look like, cd to .. and run:
+
+  perl -Mblib bench/quantone.perl foo
+
+which gives the output files a prefix of 'foo'.
+
+EOS
+
+-e 'hsvgrad.png' && -e 'rgbtile.png'
+  or die "Run makegrad.perl first";
+-e 'kscdisplay.png' 
+  or die "Missing file. Download http://www.develop-help.com/imager/kscdisplay.png";
+chdir ".." or die "Can't chdir to parent: $!";
+
+#my %qopts = qw(linsearch IM_CFLINSEARCH
+#               hashbox   IM_CFHASHBOX
+#               sortchan  IM_CFSORTCHAN
+#               rand2dist IM_CFRAND2DIST);
+
+my %qopts = qw(hashbox   IM_CFHASHBOX
+               rand2dist IM_CFRAND2DIST);
+
+
+# $bench{$opt}{$image}{$tran}{$pal} = time
+my %bench;
+$|++;
+unlink "bench/quantbench.txt";
+for my $opt (keys %qopts) {
+  open LOG, ">> bench/quantbench.log" or die "Cannot open log: $!";
+  print LOG "*** $opt ***\n";
+  close LOG;
+  $ENV{IM_CFLAGS} = "-DIM_CF_COPTS -D$qopts{$opt}";
+  print "*** $opt configuring";
+  system "perl Makefile.PL >>bench/quantbench.log 2>&1"
+    and die "Failed to configure Imager";
+  print " building";
+  system "make >>bench/quantbench.log 2>&1"
+    and die "Failed to build Imager";
+  print " benchmarking";
+  my @out = `perl -Mblib bench/quantone.perl`;
+  $? and die "Failed to run benchmark: $?";
+  chomp @out;
+  print "parsing\n";
+  my ($image, $tran);
+  foreach (@out) {
+    if (/^\*\*\s+(\S+)\s+(\S+)/) {
+      $image = $1;
+      $tran = $2;
+    }
+    elsif (/^\s*(\w+).*\@\s*([\d.]+)/ or /^\s*(\w+?):.+?([\d.]+?) CPU\)/) {
+                       print "$1: $2\n";
+      $bench{$opt}{$image}{$tran}{$1} = $2;
+    }
+    elsif (/^Benchmark:/) {
+      # ignored
+    }
+    else {
+      die "Unknown benchmark output: $_\n";
+    }
+  }
+}
+
+system "uname -srmp >bench/quantbench.txt"; # is -srmp portable?
+open BENCH, ">> bench/quantbench.txt" 
+  or die "Cannot open bench/quantbench.txt: $!";
+print BENCH Dumper(\%bench);
+close BENCH or die "Cannot close benchmark file: $!";
+print "Please email bench/quantbench.txt to tony\@develop-help.com\n";
+
+__END__
+
+=head1 NAME
+
+quantbench.pl - Benchmarks Imager's image quantization code.
+
+=head1 SYNOPSIS
+
+  perl makegrad.pl
+  perl quantbench.pl
+
+=head1 DESCRIPTION
+
+Builds Imager with various quantization code options, and then times
+the results of running with those options, with various palettes and
+options.
+
+The aim is to run this with several options on several platforms, and
+see which produces the fastest results overall.
+
+Requires that PNG and GIF (or ungif) support is present.
+
+=cut
+
diff --git a/bench/quantone.perl b/bench/quantone.perl
new file mode 100644 (file)
index 0000000..34d7280
--- /dev/null
@@ -0,0 +1,90 @@
+#!perl -w
+use Imager;
+use Benchmark;
+
+# actual benchmarking code for quantbench.pl - not intended to be used
+# directly
+
+my %imgs;
+
+my $out = shift;
+
+# rgbtile and hsvgrad are both difficult images - they both have
+# more than 256 colours
+my $img = Imager->new;
+$img->open(file=>'bench/rgbtile.png')
+  or die "Cannot load bench/rgbtile.png:",$img->errstr;
+$imgs{rgbtile} = $img;
+
+$img = Imager->new;
+$img->open(file=>'bench/hsvgrad.png')
+  or die "Cannot load bench/hsvgrad.png:", $img->errstr;
+$imgs{hsvgrad} = $img;
+
+$img = Imager->new;
+$img->open(file=>'bench/kscdisplay.png')
+  or die "Cannot load bench/kscdisplay.png:", $img->errstr;
+$imgs{kscdisplay} = $img;
+
+# I need some other images
+for my $key (keys %imgs) {
+  for my $tran (qw(closest errdiff)) {
+    my $img = $imgs{$key};
+    print "** $key $tran\n";
+    timethese(10,
+             {
+              addi=>sub {
+                $img->write(file=>out($out, $key, $tran, 'addi'), type=>'gif',
+                            gifquant=>'gen', make_colors=>'addi',
+                           translate=>$tran)
+                  or die "addi",$img->errstr;
+              },
+              webmap=>sub {
+                $img->write(file=>out($out, $key, $tran, 'webmap'), 
+                            type=>'gif',
+                            gifquant=>'gen', make_colors=>'webmap',
+                           translate=>$tran)
+                  or die "webmap",$img->errstr;
+              },
+              mono=>sub {
+                $img->write(file=>out($out, $key, $tran, 'mono'), type=>'gif',
+                            gifquant=>'gen', make_colors=>'none',
+                            colors=>[Imager::Color->new(0,0,0),
+                                     Imager::Color->new(255,255,255) ],
+                           translate=>$tran)
+                  or die "mono",$img->errstr;
+              },
+             });
+  }
+}
+
+sub out {
+  my ($out, $in, $tran, $pal) = @_;
+  $out or return '/dev/null';
+  return "bench/${out}_${in}_${tran}_$pal.gif";
+}
+
+__END__
+
+=head1 NAME
+
+quantone.perl - benchmarks image quantization with various options
+
+=head1 SYNOPSIS
+
+  # just benchmark
+  perl bench/quantone.perl
+  # produce output images too
+  perl bench/quantone.perl prefix
+
+=head1 DESCRIPTION
+
+Benchmarks image quantization on some test images, and with various
+options.
+
+The current images are 2 synthesized images (rgbtile.png and
+hsvgrad.png), and a cropped photo (kscdisplay.png).
+
+This program is designed to be run by L<quantbench.perl>.
+
+=cut
diff --git a/bigtest.perl b/bigtest.perl
new file mode 100644 (file)
index 0000000..1d45f44
--- /dev/null
@@ -0,0 +1,113 @@
+#!perl -w
+use strict;
+# tests Imager with every combination of options
+my @opts = qw(jpeg tiff png gif ungif T1-fonts TT-fonts);
+
+# each option gets a bit
+my %bits;
+@bits{@opts} = map { 1 << $_ } 0..(@opts-1);
+
+my $perl = $ENV{PERLBIN} || "perl";
+my $make = $ENV{MAKEBIN} || "make";
+my $makeopts = $ENV{MAKEOPTS} || '';
+use Getopt::Std;
+my %opts;
+getopts('vd', \%opts);
+
+my $top = (1 << @opts)-1;
+
+my @results;
+
+unlink('testout/bigtest.txt');
+my $total = 0;
+my $good = 0;
+system("$make clean") if -e 'Makefile' && !$opts{d};
+for my $set (0..$top) {
+  ++$total;
+  $ENV{IM_ENABLE} = join(' ', grep($set & $bits{$_}, @opts));
+  print STDERR $opts{v} ? "Enable: $ENV{IM_ENABLE}\n" : '.';
+  system("echo '****' \$IM_ENABLE >>testout/bigtest.txt");
+  if ($opts{d}) {
+    if (system("$make $makeopts disttest >>testout/bigtest.txt 2>&1")) {
+      push(@results, [ $ENV{IM_ENABLE}, 'disttest failed' ]);
+      next;
+    }
+  }
+  else {
+    unlink 'Makefile';
+    if (system("$perl Makefile.PL >>testout/bigtest.txt 2>&1")) {
+      push(@results, [ $ENV{IM_ENABLE}, 'Makefile.PL failed' ]);
+      next;
+    }
+    if (system("$make $makeopts >>testout/bigtest.txt 2>&1")) {
+      push(@results, [ $ENV{IM_ENABLE}, 'make failed' ]);
+      next;
+    }
+    if (system("$make test >>testout/bigtest.txt 2>&1")) {
+      push(@results, [ $ENV{IM_ENABLE}, 'test failed' ]);
+      next;
+    }
+    if (system("$make clean >>testout/bigtest.txt 2>&1")) {
+      push(@results, [ $ENV{IM_ENABLE}, 'clean failed' ]);
+      next;
+    }
+  }
+
+  push(@results, [ $ENV{IM_ENABLE}, 'success' ]);
+  ++$good;
+}
+print STDERR "\n";
+printf("%-20s %-50s\n", "Result", "Options");
+printf("%-20s %-50s\n", "-" x 20, "-" x 50);
+foreach my $row (@results) {
+  printf("%-20s %-50s\n", @$row[1,0]);
+}
+print "-" x 71, "\n";
+print "Total: $total  Successes: $good  Failures: ",$total-$good,"\n";
+print "Output in testout/bigtest.txt\n";
+
+__END__
+
+=head1 NAME
+
+  bigtest.perl - tests combinations of libraries usable by Imager
+
+=head1 SYNOPSYS
+
+  perl bigtest.perl
+  # grab a cup of coffee or four - this takes a while
+
+=head1 DESCRIPTION
+
+bigtest.perl uses the new IM_ENABLE environment variable of
+Makefile.PL to built Imager for all possible combinations of libraries
+that Imager uses.
+
+At the time of writing this is 128 combinations, which takes quite a
+while.
+
+=head1 OPTIONS
+
+ -v - verbose output
+
+ -d - perform disttest for each combination
+
+=head1 ENVIRONMENT VARIABLES
+
+PERLBIN - the perl binary to use
+
+MAKEBIN - the make binary to use
+
+Any other variables used by Imager's Makefile.PL, except for IM_MANUAL
+or IM_ENABLE.
+
+=head1 BUGS
+
+Doesn't test other possible options, like IM_NOLOG or IM_DEBUG_MALLOC.
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=cut
+
diff --git a/conv.c b/conv.c
new file mode 100644 (file)
index 0000000..b69a05e
--- /dev/null
+++ b/conv.c
@@ -0,0 +1,68 @@
+#include "image.h"
+
+/*
+  General convolution for 2d decoupled filters
+  end effects are acounted for by increasing
+  scaling the result with the sum of used coefficients
+
+  coeff: (float array) coefficients for filter
+    len: length of filter.. number of coefficients
+           note that this has to be an odd number
+           (since the filter is even);
+*/
+
+void
+i_conv(i_img *im,float *coeff,int len) {
+  int i,l,c,ch,center;
+  float pc;
+  i_color rcolor;
+  float res[11];
+  i_img timg;
+
+  mm_log((1,"i_conv(im* 0x%x,coeff 0x%x,len %d)\n",im,coeff,len));
+  i_img_empty_ch(&timg,im->xsize,im->ysize,im->channels);
+
+  center=(len-1)/2;
+
+
+  for(l=0;l<im->ysize;l++) {
+    for(i=0;i<im->xsize;i++) {
+      pc=0.0;
+      for(ch=0;ch<im->channels;ch++) res[ch]=0;
+      for(c=0;c<len;c++)
+       if (i_gpix(im,i+c-center,l,&rcolor)!=-1) {
+         for(ch=0;ch<im->channels;ch++) res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
+         pc+=coeff[c];
+       }
+      for(ch=0;ch<im->channels;ch++) rcolor.channel[ch]=(unsigned char)(((res[ch]/pc>255.0)?255.0:res[ch]/pc));
+      i_ppix(&timg,i,l,&rcolor);
+    }
+  }
+
+  for(l=0;l<im->xsize;l++)
+    {
+      for(i=0;i<im->ysize;i++)
+       {
+         pc=0.0;
+         for(ch=0;ch<im->channels;ch++) res[ch]=0;
+         for(c=0;c<len;c++)
+           if (i_gpix(&timg,l,i+c-center,&rcolor)!=-1)
+             {
+               for(ch=0;ch<im->channels;ch++) res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
+               pc+=coeff[c];
+             }
+         for(ch=0;ch<im->channels;ch++) rcolor.channel[ch]=(unsigned char)(((res[ch]/(float)(pc)>255.0)?255.0:res[ch]/(float)(pc)));
+         i_ppix(im,l,i,&rcolor);
+       }
+    }
+}
+
+
+
+
+
+
+
+
+
diff --git a/datatypes.c b/datatypes.c
new file mode 100644 (file)
index 0000000..fd13e20
--- /dev/null
@@ -0,0 +1,255 @@
+#include "io.h"
+#include "datatypes.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+
+
+/*
+  2d bitmask with test and set operations
+*/
+
+struct i_bitmap*
+btm_new(int xsize,int ysize) {
+  int i;
+  struct i_bitmap *btm;
+  btm=(struct i_bitmap*)mymalloc(sizeof(struct i_bitmap));
+  btm->data=(char*)mymalloc((xsize*ysize+8)/8);
+  btm->xsize=xsize;
+  btm->ysize=ysize;
+  for(i=0;i<(xsize*ysize+8)/8;i++) btm->data[i]=0; /* Is this always needed */
+  return btm;
+}
+
+
+void
+btm_destroy(struct i_bitmap *btm) {
+  myfree(btm->data);
+  myfree(btm);
+}
+
+
+int
+btm_test(struct i_bitmap *btm,int x,int y) {
+  int btno;
+  if (x<0 || x>btm->xsize-1 || y<0 || y>btm->ysize-1) return 0;
+  btno=btm->xsize*y+x;
+  return (1<<(btno%8))&(btm->data[btno/8]);
+}
+
+void
+btm_set(struct i_bitmap *btm,int x,int y) {
+  int btno;
+  btno=btm->xsize*y+x;
+  btm->data[btno/8]|=1<<(btno%8);
+}
+
+
+
+
+
+/*
+  Linked list - stack type 
+*/
+
+struct llink *
+llink_new(struct llink* p,int size) {
+  struct llink *l;
+  l=(struct llink*)mymalloc(sizeof(struct llink));
+  l->n=NULL;
+  l->p=p;
+  l->fill=0;
+  l->data=(void*)mymalloc(size);
+  return l;
+}
+
+/* free's the data pointer, itself, and sets the previous' next pointer to null */
+
+void
+llink_destroy(struct llink* l) {
+  if (l->p != NULL) { l->p->n=NULL; }
+  myfree(l->data);
+  myfree(l);
+}
+
+
+/* if it returns true there wasn't room for the
+   item on the link */
+
+int
+llist_llink_push(struct llist *lst, struct llink *lnk,void *data) {
+  int multip;
+  multip=lst->multip;
+
+  /*   fprintf(stderr,"llist_llink_push: data=0x%08X -> 0x%08X\n",data,*(int*)data);
+       fprintf(stderr,"ssize = %d, multip = %d, fill = %d\n",lst->ssize,lst->multip,lnk->fill); */
+  if (lnk->fill == lst->multip) return 1;
+  /*   memcpy((char*)(lnk->data)+lnk->fill*lst->ssize,data,lst->ssize); */
+  memcpy((char*)(lnk->data)+lnk->fill*lst->ssize,data,lst->ssize);
+  
+  /*   printf("data=%X res=%X\n",*(int*)data,*(int*)(lnk->data));*/
+  lnk->fill++;
+  lst->count++;
+  return 0;
+}
+
+struct llist *
+llist_new(int multip, int ssize) {
+  struct llist *l;
+  l=(struct llist*)mymalloc(sizeof(struct llist));
+  l->h=l->t=NULL;
+  l->multip=multip;
+  l->ssize=ssize;
+  l->count=0;
+  return l;
+}
+
+void
+llist_push(struct llist *l,void *data) {
+  int ssize=l->ssize;
+  int multip=l->multip;
+  
+  /*    fprintf(stderr,"llist_push: data=0x%08X\n",data); */
+
+  if (l->t == NULL) l->t=l->h=llink_new(NULL,ssize*multip);  /* Tail is empty - list is empty */
+  else { /* Check for overflow in current tail */
+    if (l->t->fill >= l->multip) {
+      struct llink* nt=llink_new(l->t,ssize*multip);
+      l->t->n=nt;
+      l->t=nt;
+      /* fprintf(stderr,"Chain extended\n"); */
+    }
+  }
+  /*   fprintf(stderr,"0x%08X\n",l->t); */
+  if (llist_llink_push(l,l->t,data)) { fprintf(stderr,"DARN!\n"); }
+}
+
+/* returns 0 if the list is empty */
+
+int
+llist_pop(struct llist *l,void *data) {
+  /*   int ssize=l->ssize; 
+       int multip=l->multip;*/
+  if (l->t == NULL) return 0;
+  l->t->fill--;
+  l->count--;
+  /*   memcpy(data,(char*)(l->t->data)+l->ssize*l->t->fill,l->ssize); */
+  memcpy(data,(char*)(l->t->data)+l->ssize*l->t->fill,l->ssize);
+  
+  if (!l->t->fill) {                           /* This link empty */
+    if (l->t->p == NULL) l->h=l->t=NULL;       /* and it's the only link */
+    else {
+      l->t=l->t->p;
+      llink_destroy(l->t->n);
+    }
+  }
+  return 1;
+}
+
+void
+llist_dump(struct llist *l) {
+  int k,j;
+  int i=0;
+  struct llink *lnk; 
+  lnk=l->h;
+  while(lnk != NULL) {
+    for(j=0;j<lnk->fill;j++) {
+      /*       memcpy(&k,(char*)(lnk->data)+l->ssize*j,sizeof(void*));*/
+      memcpy(&k,(char*)(lnk->data)+l->ssize*j,sizeof(void*));
+      printf("%d - %X\n",i,k);
+      i++;
+    }
+    lnk=lnk->n;
+  }
+}
+
+void
+llist_destroy(struct llist *l) {
+  struct llink *t,*lnk = l->h;
+  while( lnk != NULL ) {
+    t=lnk;
+    lnk=lnk->n;
+    myfree(t);
+  }
+  myfree(l);
+}
+
+
+
+
+
+
+/*
+  Oct-tree implementation 
+*/
+
+struct octt *
+octt_new() {
+  int i;
+  struct octt *t;
+  
+  t=(struct octt*)mymalloc(sizeof(struct octt));
+  for(i=0;i<8;i++) t->t[i]=NULL;
+  t->cnt=0;
+  return t;
+}
+
+
+/* returns 1 if the colors wasn't in the octtree already */
+
+
+int
+octt_add(struct octt *ct,unsigned char r,unsigned char g,unsigned char b) {
+  struct octt *c;
+  int i,cm;
+  int ci,idx[8];
+  int rc;
+  rc=0;
+  c=ct;
+  /*  printf("[r,g,b]=[%d,%d,%d]\n",r,g,b); */
+  ct->cnt++;
+  for(i=7;i>-1;i--) {
+    cm=1<<i;
+    ci=((!!(r&cm))<<2)+((!!(g&cm))<<1)+!!(b&cm); 
+    /* printf("idx[%d]=%d\n",i,ci); */
+    if (c->t[ci] == NULL) { c->t[ci]=octt_new(); rc=1; }
+    c=c->t[ci];
+    c->cnt++;
+    idx[i]=ci;
+  }
+  return rc;
+}
+
+
+void
+octt_delete(struct octt *ct) {
+  int i;
+  for(i=0;i<8;i++) if (ct->t[i] != NULL) octt_delete(ct->t[i]);  /* do not free instance here because it will free itself */
+  myfree(ct);
+}
+
+
+void
+octt_dump(struct octt *ct) {
+       int i;
+       /*      printf("node [0x%08X] -> (%d)\n",ct,ct->cnt); */
+       for(i=0;i<8;i++) if (ct->t[i] != NULL) printf("[ %d ] -> 0x%08X\n",i,(unsigned int)ct->t[i]);   
+       for(i=0;i<8;i++) if (ct->t[i] != NULL) octt_dump(ct->t[i]);
+}
+
+/* note that all calls of octt_count are operating on the same overflow 
+   variable so all calls will know at the same time if an overflow
+   has occured and stops there. */
+
+void
+octt_count(struct octt *ct,int *tot,int max,int *overflow) {
+  int i,c;
+  c=0;
+  if (!(*overflow)) return;
+  for(i=0;i<8;i++) if (ct->t[i]!=NULL) { 
+    octt_count(ct->t[i],tot,max,overflow);
+    c++;
+  }
+  if (!c) (*tot)++;
+  if ( (*tot) > (*overflow) ) *overflow=0;
+}
diff --git a/datatypes.h b/datatypes.h
new file mode 100644 (file)
index 0000000..1f18bf5
--- /dev/null
@@ -0,0 +1,122 @@
+#ifndef _DATATYPES_H_
+#define _DATATYPES_H_
+
+#include "io.h"
+
+#define MAXCHANNELS 4
+
+typedef struct { unsigned char gray_color; } gray_color;
+typedef struct { unsigned char r,g,b; } rgb_color;
+typedef struct { unsigned char r,g,b,a; } rgba_color;
+typedef struct { unsigned char c,m,y,k; } cmyk_color;
+
+typedef int undef_int; /* special value to put in typemaps to retun undef on 0 and 1 on 1 */
+
+typedef union {
+  gray_color gray;
+  rgb_color rgb;
+  rgba_color rgba;
+  cmyk_color cmyk;
+  unsigned char channel[MAXCHANNELS];
+  unsigned int ui;
+} i_color;
+
+
+struct _i_img {
+  int channels;
+  int xsize,ysize,bytes;
+  unsigned char *data;
+  unsigned int ch_mask;
+
+  int (*i_f_ppix) (struct _i_img *,int,int,i_color *); 
+  int (*i_f_gpix) (struct _i_img *,int,int,i_color *);
+  void *ext_data;
+};
+
+typedef struct _i_img i_img;
+
+/* used for palette indices in some internal code (which might be 
+   exposed at some point
+*/
+typedef unsigned char i_palidx;
+
+/* Helper datatypes
+  The types in here so far are:
+
+  doubly linked bucket list - pretty efficient
+  octtree - no idea about goodness
+  
+  needed: hashes.
+
+*/
+
+
+
+
+
+/* bitmap mask */
+
+struct i_bitmap {
+  int xsize,ysize;
+  char *data;
+};
+
+struct i_bitmap* btm_new(int xsize,int ysize);
+void btm_destroy(struct i_bitmap *btm);
+int btm_test(struct i_bitmap *btm,int x,int y);
+void btm_set(struct i_bitmap *btm,int x,int y);
+
+
+
+
+
+
+
+
+/* Stack/Linked list */
+
+struct llink {
+  struct llink *p,*n;
+  void *data;
+  int fill;            /* Number used in this link */
+};
+
+struct llist {
+  struct llink *h,*t;
+  int multip;          /* # of copies in a single chain  */
+  int ssize;           /* size of each small element     */
+  int count;           /* number of elements on the list */
+};
+
+
+/* Links */
+
+struct llink *llink_new( struct llink* p,int size );
+int  llist_llink_push( struct llist *lst, struct llink *lnk, void *data );
+
+/* Lists */
+
+struct llist *llist_new( int multip, int ssize );
+void llist_destroy( struct llist *l );
+void llist_push( struct llist *l, void *data );
+void llist_dump( struct llist *l );
+int llist_pop( struct llist *l,void *data );
+
+
+
+
+/* Octtree */
+
+struct octt {
+  struct octt *t[8]; 
+  int cnt;
+};
+
+struct octt *octt_new();
+int octt_add(struct octt *ct,unsigned char r,unsigned char g,unsigned char b);
+void octt_dump(struct octt *ct);
+void octt_count(struct octt *ct,int *tot,int max,int *overflow);
+void octt_delete(struct octt *ct);
+
+#endif
+
diff --git a/doco.perl b/doco.perl
new file mode 100755 (executable)
index 0000000..dd19597
--- /dev/null
+++ b/doco.perl
@@ -0,0 +1,76 @@
+#!/usr/bin/perl -w
+
+use Cwd;
+
+# doco.perl - 24 Jan 18:09:40 EST 2001
+#   Addi - (addi@umich.edu)
+#
+# Extract documentation and help from the source files
+# 
+#   -f <files> list FIXME comments for files
+#   -f         list FIXME comments for all files
+#   -d <file>  list pod comments from file
+
+my $comm = shift or USAGE();
+
+if ($comm eq "-f") {
+       if (!@ARGV) {
+               getfiles();
+               @files = @CFILES;
+       }
+
+       for my $file (@files) {
+               local(*FH, $/); open(FH,"$BASE/$file") or die $!;
+               my $data = <FH>; close(FH);
+               while( $data =~ m/FIXME:(.*?)\*\//sg ) {
+                       printf("%10.10s:%5d %s\n", $file, ptol($data, pos($data)), $1);
+               }
+       }
+       exit(0);
+}
+
+if ($comm eq "-d") {
+       USAGE() if !@ARGV;
+       my $file = shift; 
+       getfiles();
+       local(*FH, $/); open(FH, "$BASE/$file") or die $!;
+       my $data = <FH>; close(FH);
+       $data =~ s/^(=item)/\n$1/mg;
+       $data =~ s/^(=cut)/\n~~~~~~~~\n\n$1\n\n/mg;
+       print "\n";
+       open(FH,"|pod2text ") or die "Cannot run pod2text: $!\n";
+       print FH $data;
+       close(FH);
+       exit(2);
+}
+
+
+sub USAGE {
+
+print<<'EOF';
+doco.perl [-f files| stuff]
+
+  -f <files> list FIXME comments for files.
+  -f         list FIXME comments for all files.
+
+EOF
+       exit;
+}
+
+sub getfiles {
+       $BASE=cwd;
+       local(*FH);
+       open(FH,"$BASE/MANIFEST") or die "Cannot open MANIFEST file: $!\n";
+       my @MANIFEST = <FH>;
+       chomp(@MANIFEST);
+       @CFILES = grep { m/\.c\s*$/ } @MANIFEST;
+}
+
+# string position to line number in string
+
+sub ptol {
+       my ($str, $pos) = @_;
+       my $lcnt=1;
+       $lcnt++ while(substr($str,0,$pos)=~m/\n/g);
+       $lcnt;
+}
diff --git a/draw.c b/draw.c
new file mode 100644 (file)
index 0000000..6b1f5a6
--- /dev/null
+++ b/draw.c
@@ -0,0 +1,859 @@
+#include "image.h"
+#include "draw.h"
+#include "log.h"
+
+void
+i_mmarray_cr(i_mmarray *ar,int l) {
+  int i;
+
+  ar->lines=l;
+  ar->data=mymalloc(sizeof(minmax)*l);
+  for(i=0;i<l;i++) { ar->data[i].max=-1; ar->data[i].min=MAXINT; }
+}
+
+void
+i_mmarray_dst(i_mmarray *ar) {
+  ar->lines=0;
+  if (ar->data != NULL) { myfree(ar->data); ar->data=NULL; }
+}
+
+void
+i_mmarray_add(i_mmarray *ar,int x,int y) {
+  if (y>-1 && y<ar->lines)
+    {
+      if (x<ar->data[y].min) ar->data[y].min=x;
+      if (x>ar->data[y].max) ar->data[y].max=x;
+    }
+}
+
+int
+i_mmarray_gmin(i_mmarray *ar,int y) {
+  if (y>-1 && y<ar->lines) return ar->data[y].min;
+  else return -1;
+}
+
+int
+i_mmarray_getm(i_mmarray *ar,int y) {
+  if (y>-1 && y<ar->lines) return ar->data[y].max;
+  else return MAXINT;
+}
+
+void
+i_mmarray_render(i_img *im,i_mmarray *ar,i_color *val) {
+  int i,x;
+  for(i=0;i<ar->lines;i++) if (ar->data[i].max!=-1) for(x=ar->data[i].min;x<ar->data[i].max;x++) i_ppix(im,x,i,val);
+}
+
+
+static
+void
+i_arcdraw(int x1, int y1, int x2, int y2, i_mmarray *ar) {
+  double alpha;
+  double dsec;
+  int temp;
+  alpha=(double)(y2-y1)/(double)(x2-x1);
+  if (fabs(alpha)<1) 
+    {
+      if (x2<x1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
+      dsec=y1;
+      while(x1<x2)
+       {
+         dsec+=alpha;
+         i_mmarray_add(ar,x1,(int)(dsec+0.5));
+         x1++;
+       }
+    }
+  else
+    {
+      alpha=1/alpha;
+      if (y2<y1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
+      dsec=x1;
+      while(y1<y2)
+       {
+         dsec+=alpha;
+         i_mmarray_add(ar,(int)(dsec+0.5),y1);
+         y1++;
+       }
+    }
+}
+
+void
+i_mmarray_info(i_mmarray *ar) {
+  int i;
+  for(i=0;i<ar->lines;i++)
+  if (ar->data[i].max!=-1) printf("line %d: min=%d, max=%d.\n",i,ar->data[i].min,ar->data[i].max);
+}
+
+
+void
+i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val) {
+  i_mmarray dot;
+  float f,fx,fy;
+  int x1,y1;
+
+  mm_log((1,"i_arc(im* 0x%x,x %d,y %d,rad %.2f,d1 %.2f,d2 %.2f,val 0x%x)\n",im,x,y,rad,d1,d2,val));
+
+  i_mmarray_cr(&dot,im->ysize);
+
+  x1=(int)(x+0.5+rad*cos(d1*PI/180.0));
+  y1=(int)(y+0.5+rad*sin(d1*PI/180.0));
+  fx=(float)x1; fy=(float)y1;
+
+  /*  printf("x1: %d.\ny1: %d.\n",x1,y1); */
+  i_arcdraw(x, y, x1, y1, &dot);
+
+  x1=(int)(x+0.5+rad*cos(d2*PI/180.0));
+  y1=(int)(y+0.5+rad*sin(d2*PI/180.0));
+
+  for(f=d1;f<=d2;f+=0.01) i_mmarray_add(&dot,(int)(x+0.5+rad*cos(f*PI/180.0)),(int)(y+0.5+rad*sin(f*PI/180.0)));
+
+  /*  printf("x1: %d.\ny1: %d.\n",x1,y1); */
+  i_arcdraw(x, y, x1, y1, &dot);
+
+  /*  dot.info(); */
+  i_mmarray_render(im,&dot,val);
+}
+
+void
+i_box(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+  int x,y;
+  mm_log((1,"i_box(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
+  for(x=x1;x<x2+1;x++) {
+    i_ppix(im,x,y1,val);
+    i_ppix(im,x,y2,val);
+  }
+  for(y=y1;y<y2+1;y++) {
+    i_ppix(im,x1,y,val);
+    i_ppix(im,x2,y,val);
+  }
+}
+
+void
+i_box_filled(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+  int x,y;
+  mm_log((1,"i_box_filled(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
+  for(x=x1;x<x2+1;x++) for (y=y1;y<y2+1;y++) i_ppix(im,x,y,val);
+}
+
+
+void
+i_draw(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+  double alpha;
+  double dsec;
+  int temp;
+
+  mm_log((1,"i_draw(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
+
+  alpha=(double)(y2-y1)/(double)(x2-x1);
+  if (fabs(alpha)<1) 
+    {
+      if (x2<x1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
+      dsec=y1;
+      while(x1<x2)
+       {
+         dsec+=alpha;
+         i_ppix(im,x1,(int)(dsec+0.5),val);
+         x1++;
+       }
+    }
+  else
+    {
+      alpha=1/alpha;
+      if (y2<y1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
+      dsec=x1;
+      while(y1<y2)
+       {
+         dsec+=alpha;
+         i_ppix(im,(int)(dsec+0.5),y1,val);
+         y1++;
+       }
+    }
+  mm_log((1,"i_draw: alpha=%f.\n",alpha));
+}
+
+void
+i_line_aa(i_img *im,int x1,int y1,int x2,int y2,i_color *val) {
+  i_color tval;
+  float alpha;
+  float dsec,dfrac;
+  int temp,dx,dy,isec,ch;
+
+  mm_log((1,"i_draw(im* 0x%x,x1 %d,y1 %d,x2 %d,y2 %d,val 0x%x)\n",im,x1,y1,x2,y2,val));
+
+  dy=y2-y1;
+  dx=x2-x1;
+
+  if (abs(dx)>abs(dy)) { /* alpha < 1 */
+    if (x2<x1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
+    alpha=(float)(y2-y1)/(float)(x2-x1);
+
+    dsec=y1;
+    while(x1<=x2) {
+      isec=(int)dsec;
+      dfrac=dsec-isec;
+      /*      dfrac=1-(1-dfrac)*(1-dfrac); */
+      /* This is something we can play with to try to get better looking lines */
+
+      i_gpix(im,x1,isec,&tval);
+      for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)(dfrac*(float)tval.channel[ch]+(1-dfrac)*(float)val->channel[ch]);
+      i_ppix(im,x1,isec,&tval);
+      
+      i_gpix(im,x1,isec+1,&tval);
+      for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)((1-dfrac)*(float)tval.channel[ch]+dfrac*(float)val->channel[ch]);
+      i_ppix(im,x1,isec+1,&tval);
+      
+      dsec+=alpha;
+      x1++;
+    }
+  } else {
+    if (y2<y1) { temp=y1; y1=y2; y2=temp; temp=x1; x1=x2; x2=temp; }
+    alpha=(float)(x2-x1)/(float)(y2-y1);
+    dsec=x1;
+    while(y1<=y2) {
+      isec=(int)dsec;
+      dfrac=dsec-isec;
+      /*      dfrac=sqrt(dfrac); */
+      /* This is something we can play with */
+      i_gpix(im,isec,y1,&tval);
+      for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)(dfrac*(float)tval.channel[ch]+(1-dfrac)*(float)val->channel[ch]);
+      i_ppix(im,isec,y1,&tval);
+
+      i_gpix(im,isec+1,y1,&tval);
+      for(ch=0;ch<im->channels;ch++) tval.channel[ch]=(unsigned char)((1-dfrac)*(float)tval.channel[ch]+dfrac*(float)val->channel[ch]);
+      i_ppix(im,isec+1,y1,&tval);
+
+      dsec+=alpha;
+      y1++;
+    }
+  }
+}
+
+double
+perm(int n,int k) {
+  double r;
+  int i;
+  r=1;
+  for(i=k+1;i<=n;i++) r*=i;
+  for(i=1;i<=(n-k);i++) r/=i;
+  return r;
+}
+
+
+/* Note in calculating t^k*(1-t)^(n-k) 
+   we can start by using t^0=1 so this simplifies to
+   t^0*(1-t)^n - we want to multiply that with t/(1-t) each iteration
+   to get a new level - this may lead to errors who knows lets test it */
+
+void
+i_bezier_multi(i_img *im,int l,double *x,double *y,i_color *val) {
+  double *bzcoef;
+  double t,cx,cy;
+  int k,i;
+  int lx = 0,ly = 0;
+  int n=l-1;
+  double itr,ccoef;
+
+
+  bzcoef=mymalloc(sizeof(double)*l);
+  for(k=0;k<l;k++) bzcoef[k]=perm(n,k);
+  ICL_info(val);
+
+
+  /*  for(k=0;k<l;k++) printf("bzcoef: %d -> %f\n",k,bzcoef[k]); */
+  i=0;
+  for(t=0;t<=1;t+=0.005) {
+    cx=cy=0;
+    itr=t/(1-t);
+    ccoef=pow(1-t,n);
+    for(k=0;k<l;k++) {
+      /*      cx+=bzcoef[k]*x[k]*pow(t,k)*pow(1-t,n-k); 
+             cy+=bzcoef[k]*y[k]*pow(t,k)*pow(1-t,n-k);*/
+
+      cx+=bzcoef[k]*x[k]*ccoef;
+      cy+=bzcoef[k]*y[k]*ccoef;
+      ccoef*=itr;
+    }
+    /*    printf("%f -> (%d,%d)\n",t,(int)(0.5+cx),(int)(0.5+cy)); */
+    if (i++) { 
+      i_line_aa(im,lx,ly,(int)(0.5+cx),(int)(0.5+cy),val);
+    }
+      /*     i_ppix(im,(int)(0.5+cx),(int)(0.5+cy),val); */
+    lx=(int)(0.5+cx);
+    ly=(int)(0.5+cy);
+  }
+  ICL_info(val);
+  myfree(bzcoef);
+}
+
+
+
+/* Flood fill 
+
+   REF: Graphics Gems I. page 282+
+
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* This should be moved into a seperate file? */
+
+/* This is the truncation used:
+   
+   a double is multiplied by 16 and then truncated.
+   This means that 0 -> 0
+   So a triangle of (0,0) (10,10) (10,0) Will look like it's
+   not filling the (10,10) point nor the (10,0)-(10,10)  line segment
+
+*/
+
+
+
+
+#define IMTRUNC(x) ((int)(x*16))
+
+
+/*
+typedef struct {
+  short ms,ls;
+} pcord;
+*/
+
+typedef int pcord;
+
+struct p_point {
+  int n;
+  pcord x,y;
+};
+
+struct p_line  {
+  int n;
+  pcord x1,y1;
+  pcord x2,y2;
+  pcord miny,maxy;
+};
+
+struct p_slice {
+  int n;
+  double x;
+};
+
+int
+p_compy(const struct p_point *p1, const struct p_point *p2) {
+  if (p1->y > p2->y) return 1;
+  if (p1->y < p2->y) return -1;
+  return 0;
+}
+
+int
+p_compx(const struct p_slice *p1, const struct p_slice *p2) {
+  if (p1->x > p2->x) return 1;
+  if (p1->x < p2->x) return -1;
+  return 0;
+}
+
+/* Change this to int? and round right goddamn it! */
+
+double
+p_eval_aty(struct p_line *l,pcord y) {
+  int t;
+  t=l->y2-l->y1;
+  if (t) return ( (y-l->y1)*l->x2 + (l->y2-y)*l->x1 )/t;
+  return (l->x1+l->x2)/2.0;
+}
+
+double
+p_eval_atx(struct p_line *l,pcord x) {
+  int t;
+  t=l->x2-l->x1;
+  if (t) return ( (x-l->x1)*l->y2 + (l->x2-x)*l->y1 )/t;
+  return (l->y1+l->y2)/2.0;
+}
+
+
+/* Algorithm to count the pixels covered by line going through pixel (x,y)
+   in coarse coords.
+*/
+
+/*
+static int
+p_eval_coverage(struct p_line *l, int lc, int x, pcord y1, pcord y2) {
+
+  return 0;
+}
+*/
+
+
+/* Antialiasing polygon algorithm 
+   specs:
+     1. only nice polygons - no crossovers
+     2. 1/16 pixel resolution # previously - floating point co-ordinates
+     3. full antialiasing ( complete spectrum of blends )
+     4. uses hardly any memory
+     5. no subsampling phase
+
+   For each interval we must: 
+     1. find which lines are in it
+     2. order the lines from in increasing x order.
+        since we are assuming no crossovers it is sufficent
+        to check a single point on each line.
+*/
+
+/*
+  Terms:
+  
+    1. Interval:  A vertical segment in which no lines cross nor end.
+    2. Scanline:  A physical line, contains 16 subpixels in the horizontal direction
+    3. Slice:     A start stop line pair.
+  
+ */
+
+/* Templine logic: 
+   
+   The variable tempflush describes if there is anything in the templine array or not.
+   
+   if tempflush is 0 then the array is clean.
+   if tempflush is 1 then the array contains a partial filled scanline
+   
+ */
+
+/* Rendering of a single start stop pair:
+
+?? REWRITE
+   
+   The rendering is split in three parts
+   1. From the first start pixel to the first stop pixel
+   2. Area from the first end pixel to the last start pixel
+   3. Area from the first end pixel to the last start pixel
+
+ */
+
+
+void
+i_poly_aa(i_img *im,int l,double *x,double *y,i_color *val) {
+  int i,k;                     /* Index variables */
+  int clc;                     /* Index of next item on interval linelist */
+  int tx;                      /* Coarse x coord within a scanline */
+  pcord miny,maxy;             /* Min and max values of the current slice in the subcord system */
+  pcord minacy,maxacy;         /* Min and max values of the current scanline bounded by the slice
+                                  in the subcord system */
+  int cscl;                    /* Current scanline */
+  pcord cc;                    /* Current vertical centerpoint of interval */
+  int mt1,mt2;
+  int minsx,minex,maxsx,maxex; /* The horizontal stretches of the lines beloning to the current slice within a scanline */
+  int *templine;               /* Line accumulator */
+
+  struct p_point *pset;                /* List of points in polygon */
+  struct p_line *lset;         /* List of lines in polygon */
+  struct p_slice *tllist;      /* List of slices */
+
+  i_color red,blue,yellow;
+  red.rgb.r=255;
+  red.rgb.g=0;
+  red.rgb.b=0;
+
+  blue.rgb.r=0;
+  blue.rgb.g=0;
+  blue.rgb.b=255;
+
+  yellow.rgb.r=255;
+  yellow.rgb.g=255;
+  yellow.rgb.b=255;
+  
+  if ( (pset=mymalloc(sizeof(struct p_point)*l)) == NULL) { m_fatal(2,"malloc failed\n"); return; }
+  if ( (lset=mymalloc(sizeof(struct p_line)*l)) == NULL) { m_fatal(2,"malloc failed\n"); return; }
+  if ( (tllist=mymalloc(sizeof(struct p_slice)*l)) == NULL) { m_fatal(2,"malloc failed\n"); return; }
+  if ( (templine=mymalloc(sizeof(int)*im->xsize)) == NULL) { m_fatal(2,"malloc failed\n"); return; }
+
+  /* insert the lines into the line list */
+  
+  for(i=0;i<l;i++) {
+    pset[i].n=i;
+    pset[i].x=IMTRUNC(x[i]);
+    pset[i].y=IMTRUNC(y[i]);
+    lset[i].n=i;
+    lset[i].x1=IMTRUNC(x[i]);
+    lset[i].y1=IMTRUNC(y[i]);
+    lset[i].x2=IMTRUNC(x[(i+1)%l]);
+    lset[i].y2=IMTRUNC(y[(i+1)%l]);
+    lset[i].miny=min(lset[i].y1,lset[i].y2);
+    lset[i].maxy=max(lset[i].y1,lset[i].y2);
+  }
+  
+  qsort(pset,l,sizeof(struct p_point),(int(*)(const void *,const void *))p_compy);
+  
+  printf("post point list (sorted in ascending y order)\n");
+  for(i=0;i<l;i++) {
+    printf("%d [ %d ] %d %d\n",i,pset[i].n,pset[i].x,pset[i].y);
+  }
+  
+  printf("line list\n");
+  for(i=0;i<l;i++) {
+    printf("%d [ %d ] (%d , %d) -> (%d , %d) yspan ( %d , %d )\n",i,lset[i].n,lset[i].x1,lset[i].y1,lset[i].x2,lset[i].y2,lset[i].miny,lset[i].maxy);
+  }
+
+  printf("MAIN LOOP\n\n");
+
+  /* Zero templine buffer */
+  /* Templine buffer flushed everytime a scan line ends */
+  for(i=0;i<im->xsize;i++) templine[i]=0;
+  
+
+  /* loop on intervals */
+  for(i=0;i<l-1;i++) {
+    cc=(pset[i].y+pset[i+1].y)/2;
+    printf("current slice is: %d to %d ( cpoint %d )\n",pset[i].y,pset[i+1].y,cc);
+    clc=0;
+
+    /* stuff this in a function ?? */
+
+    /* Check what lines belong to interval */
+    for(k=0;k<l;k++) {
+      printf("checking line: %d [ %d ] (%d , %d) -> (%d, %d) yspan ( %d , %d )",
+            k,lset[k].n,lset[k].x1,lset[k].y1,lset[k].x2,lset[k].y2,lset[k].miny,lset[k].maxy);
+      if (cc >= lset[k].miny && cc <=  lset[k].maxy) {
+       if (lset[k].miny == lset[k].maxy) printf(" HORIZONTAL - skipped\n");
+       else {
+         printf(" INSIDE\n"); 
+         tllist[clc].x=p_eval_aty(&lset[k],cc);
+         tllist[clc++].n=k;
+       }
+      } else printf(" OUTSIDE\n");
+    }
+    
+    /* 
+       at this point a table of pixels that need special care should
+       be generated from the line list - it should be ordered so that only
+       one needs to be checked - options: rendering to a list then order - or
+       rendering in the right order might be possible to do nicely with the
+       following heuristic:
+       
+       1. Draw leftmost pixel for this line
+       2. If preceeding pixel was occupied check next one else go to 1 again.
+    */
+    
+    printf("lines in current interval:");
+    for(k=0;k<clc;k++) printf("  %d (%.2f)",tllist[k].n,tllist[k].x);
+    printf("\n");
+    
+    /* evaluate the lines in the middle of the slice */
+    
+    printf("Sort lines left to right within interval\n");
+    qsort(tllist,clc,sizeof(struct p_slice),(int(*)(const void *,const void *))p_compx);
+    
+    printf("sorted lines in interval - output:");
+    for(k=0;k<clc;k++) printf(" %d",tllist[k].n);
+    printf("\n");
+    
+    miny=pset[i].y;
+    maxy=pset[i+1].y;
+
+    /* iterate over scanlines */
+    for(cscl=(miny)/16;cscl<=maxy/16;cscl++) {
+      minacy=max(miny,cscl*16);
+      maxacy=min(maxy,cscl*16+15);
+      
+      printf("Scanline bound %d - %d\n",minacy, maxacy);
+
+      /* iterate over line pairs (slices) within interval */
+      for(k=0;k<clc-1;k+=2) {
+
+       mt1=p_eval_aty(&lset[tllist[k].n],minacy);      /* upper corner */
+       mt2=p_eval_aty(&lset[tllist[k].n],maxacy);      /* lower corner */
+       minsx=min(mt1,mt2);
+       minex=max(mt1,mt2);
+       mt1=p_eval_aty(&lset[tllist[k+1].n],minacy);    /* upper corner */
+       mt2=p_eval_aty(&lset[tllist[k+1].n],maxacy);    /* lower corner */
+       maxsx=min(mt1,mt2);
+       maxex=max(mt1,mt2);
+
+       printf("minsx: %d minex: %d\n",minsx,minex);
+       printf("maxsx: %d maxex: %d\n",maxsx,maxex);
+       
+       if (minex/16<maxsx/16) printf("Scan slice is simple!\n");
+       else printf("Scan slice is complicated!\n");
+       
+       if (minsx/16 == minex/16) { /* The line starts and ends in the same pixel */
+         printf("Low slant start pixel\n");
+         templine[minsx/16]=(maxacy-minacy+1)*(minex-minsx+1)/2+((minex | 0xF)-minex)*(maxacy-minacy+1);
+       } else {
+         for(tx=minsx/16;tx<minex/16+1;tx++) {
+           int minx,maxx,minxy,maxxy;
+           minx=max(minsx,  tx*16   );
+           maxx=min(minex,  tx*16+15);
+
+           if (minx == maxx) {
+             templine[tx]=(maxacy-minacy+1);
+           } else {
+           
+             minxy=p_eval_atx(&lset[tllist[k].n], minx);
+             maxxy=p_eval_atx(&lset[tllist[k].n], maxx);
+             
+             templine[tx]+=(abs(minxy-maxxy)+1)*(minex-minsx+1)/2; /* The triangle between the points */
+             if (mt1 < mt2) { /* \ slant */
+               /* ((minex | 0xF)-minex)*(maxacy-minacy+1); FIXME: unfinished */
+               
+
+
+             } else {
+               templine[tx]+=((minex | 0xF)-minex)*(maxacy-minacy+1);
+             }
+             
+           }
+         }
+       }
+
+       for(tx=maxsx/16;tx<maxex/16+1;tx++) templine[tx]+=16*(maxacy-minacy+1);
+       
+       /*      for(tx=minex/16+1;tx<maxsx/16;tx++) 0; */
+       
+       
+       printf("line %d: painting  %d - %d\n",cscl,minex/16+1,maxsx/16);
+       if ( (minacy != cscl*16) || (maxacy != cscl*16+15) ) {
+         for(tx=minsx/16;tx<maxex/16+1;tx++) {
+           i_ppix(im,tx,cscl,&yellow);
+         }
+       }
+       else {
+         for(tx=minsx/16;tx<minex/16+1;tx++) i_ppix(im,tx,cscl,&red);
+         for(tx=maxsx/16;tx<maxex/16+1;tx++) i_ppix(im,tx,cscl,&blue);
+         for(tx=minex/16+1;tx<maxsx/16;tx++) i_ppix(im,tx,cscl,val);
+       }
+
+      } /* Slices */
+    } /* Scanlines */
+  } /* Intervals */
+} /* Function */
+
+
+
+
+
+
+
+/* Flood fill algorithm - based on the Ken Fishkins (pixar) gem in 
+   graphics gems I */
+
+/*
+struct stc {
+  int mylx,myrx; 
+  int dadlx,dadrx;
+  int myy;
+  int mydirection;
+};
+
+Not used code???
+*/
+
+
+struct stack_element {
+  int myLx,myRx;
+  int dadLx,dadRx;
+  int myY;
+  int myDirection;
+};
+
+
+/* create the link data to put push onto the stack */
+
+static
+struct stack_element*
+crdata(int left,int right,int dadl,int dadr,int y, int dir) {
+  struct stack_element *ste;
+  ste=(struct stack_element*)mymalloc(sizeof(struct stack_element));
+  ste->myLx=left;
+  ste->myRx=right;
+  ste->dadLx=dadl;
+  ste->dadRx=dadr;
+  ste->myY=y;
+  ste->myDirection=dir;
+  return ste;
+}
+
+/* i_ccomp compares two colors and gives true if they are the same */
+
+static int
+i_ccomp(i_color *val1,i_color *val2,int ch) {
+  int i;
+  for(i=0;i<ch;i++) if (val1->channel[i] !=val2->channel[i]) return 0;
+  return 1;
+}
+
+
+static int
+i_lspan(i_img *im,int seedx,int seedy,i_color *val) {
+  i_color cval;
+  while(1) {
+    if (seedx-1 < 0) break;
+    i_gpix(im,seedx-1,seedy,&cval);
+    if (!i_ccomp(val,&cval,im->channels)) break;
+    seedx--;
+  }
+  return seedx;
+}
+
+static int
+i_rspan(i_img *im,int seedx,int seedy,i_color *val) {
+  i_color cval;
+  while(1) {
+    if (seedx+1 > im->xsize-1) break;
+    i_gpix(im,seedx+1,seedy,&cval);
+    if (!i_ccomp(val,&cval,im->channels)) break;
+    seedx++;
+  }
+  return seedx;
+}
+
+/* Macro to create a link and push on to the list */
+
+#define ST_PUSH(left,right,dadl,dadr,y,dir) { struct stack_element *s=crdata(left,right,dadl,dadr,y,dir); llist_push(st,&s);}
+
+/* pops the shadow on TOS into local variables lx,rx,y,direction,dadLx and dadRx */
+/* No overflow check! */
+#define ST_POP() { struct stack_element *s; llist_pop(st,&s); lx=s->myLx; rx=s->myRx; dadLx=s->dadLx; dadRx=s->dadRx; y=s->myY; direction=s->myDirection; myfree(s); }
+
+#define ST_STACK(dir,dadLx,dadRx,lx,rx,y) { int pushrx=rx+1; int pushlx=lx-1; ST_PUSH(lx,rx,pushlx,pushrx,y+dir,dir); if (rx > dadRx) ST_PUSH(dadRx+1,rx,pushlx,pushrx,y-dir,-dir); if (lx < dadLx) ST_PUSH(lx,dadLx-1,pushlx,pushrx,y-dir,-dir); }
+
+#define SET(x,y) btm_set(btm,x,y);
+
+#define INSIDE(x,y) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),i_ccomp(&val,&cval,channels)  ) ))
+
+void
+i_flood_fill(i_img *im,int seedx,int seedy,i_color *dcol) {
+
+  int lx,rx;
+  int y;
+  int direction;
+  int dadLx,dadRx;
+
+  int wasIn=0;
+  int x=0;
+
+  /*  int tx,ty; */
+
+  int bxmin=seedx,bxmax=seedx,bymin=seedy,bymax=seedy;
+
+  struct llist *st;
+  struct i_bitmap *btm;
+
+  int channels,xsize,ysize;
+  i_color cval,val;
+
+  channels=im->channels;
+  xsize=im->xsize;
+  ysize=im->ysize;
+
+  btm=btm_new(xsize,ysize);
+  st=llist_new(100,sizeof(struct stack_element*));
+
+  /* Get the reference color */
+  i_gpix(im,seedx,seedy,&val);
+
+  /* Find the starting span and fill it */
+  lx=i_lspan(im,seedx,seedy,&val);
+  rx=i_rspan(im,seedx,seedy,&val);
+  
+  /* printf("span: %d %d \n",lx,rx); */
+
+  for(x=lx;x<=rx;x++) SET(x,seedy);
+
+  ST_PUSH(lx,rx,lx,rx,seedy+1,1);
+  ST_PUSH(lx,rx,lx,rx,seedy-1,-1);
+
+  while(st->count) {
+    ST_POP();
+    
+    if (y<0 || y>ysize-1) continue;
+
+    if (bymin > y) bymin=y; /* in the worst case an extra line */
+    if (bymax < y) bymax=y; 
+
+    /*     printf("start of scan - on stack : %d \n",st->count); */
+
+    
+    /*     printf("lx=%d rx=%d dadLx=%d dadRx=%d y=%d direction=%d\n",lx,rx,dadLx,dadRx,y,direction); */
+    
+    /*
+    printf(" ");
+    for(tx=0;tx<xsize;tx++) printf("%d",tx%10);
+    printf("\n");
+    for(ty=0;ty<ysize;ty++) {
+      printf("%d",ty%10);
+      for(tx=0;tx<xsize;tx++) printf("%d",!!btm_test(btm,tx,ty));
+      printf("\n");
+    }
+
+    printf("y=%d\n",y);
+    */
+
+
+    x=lx+1;
+    if ( (wasIn = INSIDE(lx,y)) ) {
+      SET(lx,y);
+      lx--;
+      while(INSIDE(lx,y) && lx > 0) {
+       SET(lx,y);
+       lx--;
+      }
+    }
+
+    if (bxmin > lx) bxmin=lx;
+    
+    while(x <= xsize-1) {
+      /*  printf("x=%d\n",x); */
+      if (wasIn) {
+       
+       if (INSIDE(x,y)) {
+         /* case 1: was inside, am still inside */
+         SET(x,y);
+       } else {
+         /* case 2: was inside, am no longer inside: just found the
+            right edge of a span */
+         ST_STACK(direction,dadLx,dadRx,lx,(x-1),y);
+
+         if (bxmax < x) bxmax=x;
+
+         wasIn=0;
+       }
+      } else {
+       if (x>rx) goto EXT;
+       if (INSIDE(x,y)) {
+         SET(x,y);
+         /* case 3: Wasn't inside, am now: just found the start of a new run */
+         wasIn=1;
+           lx=x;
+       } else {
+         /* case 4: Wasn't inside, still isn't */
+       }
+      }
+      x++;
+    }
+  EXT: /* out of loop */
+    if (wasIn) {
+      /* hit an edge of the frame buffer while inside a run */
+      ST_STACK(direction,dadLx,dadRx,lx,(x-1),y);
+      if (bxmax < x) bxmax=x;
+    }
+  }
+  
+  /*   printf("lx=%d rx=%d dadLx=%d dadRx=%d y=%d direction=%d\n",lx,rx,dadLx,dadRx,y,direction); 
+       printf("bounding box: [%d,%d] - [%d,%d]\n",bxmin,bymin,bxmax,bymax); */
+
+  for(y=bymin;y<=bymax;y++) for(x=bxmin;x<=bxmax;x++) if (btm_test(btm,x,y)) i_ppix(im,x,y,dcol);
+
+  btm_destroy(btm);
+  llist_destroy(st);
+}
diff --git a/draw.h b/draw.h
new file mode 100644 (file)
index 0000000..3856733
--- /dev/null
+++ b/draw.h
@@ -0,0 +1,24 @@
+#include "image.h"
+
+typedef struct {
+  int min,max;
+}  minmax;
+
+typedef struct {
+  minmax *data;
+  int lines;
+} i_mmarray;
+
+/* FIXME: Merge this into datatypes.{c,h} */
+
+void i_mmarray_cr(i_mmarray *ar,int l);
+void i_mmarray_dst(i_mmarray *ar);
+void i_mmarray_add(i_mmarray *ar,int x,int y);
+int i_mmarray_gmin(i_mmarray *ar,int y);
+int i_mmarray_getm(i_mmarray *ar,int y);
+void i_mmarray_render(i_img *im,i_mmarray *ar,i_color *val);
+static void i_arcdraw(int x1, int y1, int x2, int y2, i_mmarray *ar);
+void i_mmarray_info(i_mmarray *ar);
+void i_arc(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val);
+void i_box(i_img *im,int x0,int y0,int x1,int y1,i_color *val);
+void i_draw(i_img *im,int x1,int y1,int x2,int y2,i_color *val);
diff --git a/dynaload.c b/dynaload.c
new file mode 100644 (file)
index 0000000..b5486a5
--- /dev/null
@@ -0,0 +1,301 @@
+#include "dynaload.h"
+#include "XSUB.h" /* so we can compile on threaded perls */
+
+/* These functions are all shared - then comes platform dependant code */
+
+
+int getstr(void *hv_t,char *key,char **store) {
+  SV** svpp;
+  HV* hv=(HV*)hv_t;
+
+  mm_log((1,"getstr(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=SvPV(*svpp, PL_na );
+
+  return 1;
+}
+
+int getint(void *hv_t,char *key,int *store) {
+  SV** svpp;
+  HV* hv=(HV*)hv_t;  
+
+  mm_log((1,"getint(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=(int)SvIV(*svpp);
+  return 1;
+}
+
+int getdouble(void *hv_t,char* key,double *store) {
+  SV** svpp;
+  HV* hv=(HV*)hv_t;
+
+  mm_log((1,"getdouble(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=(float)SvNV(*svpp);
+  return 1;
+}
+
+int getvoid(void *hv_t,char* key,void **store) {
+  SV** svpp;
+  HV* hv=(HV*)hv_t;
+
+  mm_log((1,"getvoid(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=(void*)SvIV(*svpp);
+
+  return 1;
+}
+
+int getobj(void *hv_t,char *key,char *type,void **store) {
+  SV** svpp;
+  HV* hv=(HV*)hv_t;
+
+  mm_log((1,"getobj(hv_t 0x%X, key %s,type %s, store 0x%X)\n",hv_t,key,type,store));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+
+  if (sv_derived_from(*svpp,type)) {
+    IV tmp = SvIV((SV*)SvRV(*svpp));
+    *store = (void*) tmp;
+  } else {
+    mm_log((1,"getobj: key exists in hash but is not of correct type"));
+    return 0;
+  }
+
+  return 1;
+}
+
+
+UTIL_table_t UTIL_table={getstr,getint,getdouble,getvoid,getobj};
+extern symbol_table_t symbol_table;
+
+/*
+  Dynamic loading works like this:
+  dynaload opens the shared object and
+  loads all the functions into an array of functions
+  it returns a string from the dynamic function that
+  can be supplied to the parser for evaling.
+*/
+
+void
+DSO_call(DSO_handle *handle,int func_index,HV* hv) {
+  mm_log((1,"DSO_call(handle 0x%X, func_index %d, hv 0x%X)\n",handle,func_index,hv));
+  (handle->function_list[func_index].iptr)((void*)hv);
+}
+
+
+#if defined( OS_hpux )
+
+void*
+DSO_open(char* file,char** evalstring) {
+  shl_t tt_handle;
+  void *d_handle,**plugin_symtab,**plugin_utiltab;
+  int  rc,*iptr, (*fptr)(int);
+  func_ptr *function_list;
+  DSO_handle *dso_handle;
+  void (*f)(void *s,void *u); /* these will just have to be void for now */
+  int i;
+
+  *evalstring=NULL;
+
+  mm_log( (1,"DSO_open(file '%s' (0x%08X), evalstring 0x%08X)\n",file,file,evalstring) );
+
+  if ( (tt_handle = shl_load(file, BIND_DEFERRED,0L)) == NULL) return NULL; 
+  if ( (shl_findsym(&tt_handle, I_EVALSTR,TYPE_UNDEFINED,(void*)evalstring))) return NULL;
+
+  /*
+  if ( (shl_findsym(&tt_handle, "symbol_table",TYPE_UNDEFINED,(void*)&plugin_symtab))) return NULL;
+  if ( (shl_findsym(&tt_handle, "util_table",TYPE_UNDEFINED,&plugin_utiltab))) return NULL;
+  (*plugin_symtab)=&symbol_table;
+  (*plugin_utiltab)=&UTIL_table;
+  */
+
+  if ( (shl_findsym(&tt_handle, I_INSTALL_TABLES ,TYPE_UNDEFINED, &f ))) return NULL; 
+  mm_log( (1,"Calling install_tables\n") );
+  f(&symbol_table,&UTIL_table);
+  mm_log( (1,"Call ok.\n") ); 
+  if ( (shl_findsym(&tt_handle, I_FUNCTION_LIST ,TYPE_UNDEFINED,(func_ptr*)&function_list))) return NULL; 
+  if ( (dso_handle=(DSO_handle*)malloc(sizeof(DSO_handle))) == NULL) return NULL;
+
+  dso_handle->handle=tt_handle; /* needed to close again */
+  dso_handle->function_list=function_list;
+  if ( (dso_handle->filename=(char*)malloc(strlen(file))) == NULL) { free(dso_handle); return NULL; }
+  strcpy(dso_handle->filename,file);
+
+  mm_log((1,"DSO_open <- (0x%X)\n",dso_handle));
+  return (void*)dso_handle;
+}
+
+undef_int
+DSO_close(void *ptr) {
+  DSO_handle *handle=(DSO_handle*) ptr;
+  mm_log((1,"DSO_close(ptr 0x%X)\n",ptr));
+  return !shl_unload((handle->handle));
+}
+
+#elif defined(WIN32)
+
+void *
+DSO_open(char *file, char **evalstring) {
+  HMODULE d_handle;
+  func_ptr *function_list;
+  DSO_handle *dso_handle;
+  
+  void (*f)(void *s,void *u); /* these will just have to be void for now */
+
+  mm_log( (1,"DSO_open(file '%s' (0x%08X), evalstring 0x%08X)\n",file,file,evalstring) );
+
+  *evalstring = NULL;
+  if ((d_handle = LoadLibrary(file)) == NULL) {
+    mm_log((1, "DSO_open: LoadLibrary(%s) failed: %lu\n", file, GetLastError()));
+    return NULL;
+  }
+  if ( (*evalstring = (char *)GetProcAddress(d_handle, I_EVALSTR)) == NULL) {
+    mm_log((1,"DSO_open: GetProcAddress didn't fine '%s': %lu\n", I_EVALSTR, GetLastError()));
+    FreeLibrary(d_handle);
+    return NULL;
+  }
+  if ((f = (void (*)(void *, void*))GetProcAddress(d_handle, I_INSTALL_TABLES)) == NULL) {
+    mm_log((1, "DSO_open: GetProcAddress didn't find '%s': %lu\n", I_INSTALL_TABLES, GetLastError()));
+    FreeLibrary(d_handle);
+    return NULL;
+  }
+  mm_log((1, "Calling install tables\n"));
+  f(&symbol_table, &UTIL_table);
+  mm_log((1, "Call ok\n"));
+  
+  if ( (function_list = (func_ptr *)GetProcAddress(d_handle, I_FUNCTION_LIST)) == NULL) {
+    mm_log((1, "DSO_open: GetProcAddress didn't find '%s': %lu\n", I_FUNCTION_LIST, GetLastError()));
+    FreeLibrary(d_handle);
+    return NULL;
+  }
+  if ( (dso_handle = (DSO_handle*)malloc(sizeof(DSO_handle))) == NULL) {
+    mm_log( (1, "DSO_Open: out of memory\n") );
+    FreeLibrary(d_handle);
+    return NULL;
+  }
+  dso_handle->handle=d_handle; /* needed to close again */
+  dso_handle->function_list=function_list;
+  if ( (dso_handle->filename=(char*)malloc(strlen(file))) == NULL) { free(dso_handle); FreeLibrary(d_handle); return NULL; }
+  strcpy(dso_handle->filename,file);
+
+  mm_log( (1,"DSO_open <- 0x%X\n",dso_handle) );
+  return (void*)dso_handle;
+
+}
+
+undef_int
+DSO_close(void *ptr) {
+  DSO_handle *handle = (DSO_handle *)ptr;
+  FreeLibrary(handle->handle);
+  free(handle->filename);
+  free(handle);
+}
+
+#else
+
+/* OS/2 has no dlclose; Perl doesn't provide one. */
+#ifdef __EMX__ /* OS/2 */
+int
+dlclose(minthandle_t h) {
+  return DosFreeModule(h) ? -1 : 0;
+}
+#endif /* __EMX__ */
+
+
+void*
+DSO_open(char* file,char** evalstring) {
+  void *d_handle;
+  func_ptr *function_list;
+  DSO_handle *dso_handle;
+
+  void (*f)(void *s,void *u); /* these will just have to be void for now */
+  
+  *evalstring=NULL;
+
+  mm_log( (1,"DSO_open(file '%s' (0x%08X), evalstring 0x%08X)\n",file,file,evalstring) );
+
+  if ( (d_handle = dlopen(file, RTLD_LAZY)) == NULL) {
+    mm_log( (1,"DSO_open: dlopen failed: %s.\n",dlerror()) );
+    return NULL;
+  }
+
+  if ( (*evalstring = (char *)dlsym(d_handle, I_EVALSTR)) == NULL) {
+    mm_log( (1,"DSO_open: dlsym didn't find '%s': %s.\n",I_EVALSTR,dlerror()) );
+    return NULL;
+  }
+
+  /*
+
+    I'll just leave this thing in here for now if I need it real soon
+
+   mm_log( (1,"DSO_open: going to dlsym '%s'\n", I_SYMBOL_TABLE ));
+   if ( (plugin_symtab = dlsym(d_handle, I_SYMBOL_TABLE)) == NULL) {
+     mm_log( (1,"DSO_open: dlsym didn't find '%s': %s.\n",I_SYMBOL_TABLE,dlerror()) );
+     return NULL;
+   }
+  
+   mm_log( (1,"DSO_open: going to dlsym '%s'\n", I_UTIL_TABLE ));
+    if ( (plugin_utiltab = dlsym(d_handle, I_UTIL_TABLE)) == NULL) {
+     mm_log( (1,"DSO_open: dlsym didn't find '%s': %s.\n",I_UTIL_TABLE,dlerror()) );
+     return NULL;
+   }
+
+  */
+
+  f = (void(*)(void *s,void *u))dlsym(d_handle, I_INSTALL_TABLES);
+  mm_log( (1,"DSO_open: going to dlsym '%s'\n", I_INSTALL_TABLES ));
+  if ( (f = (void(*)(void *s,void *u))dlsym(d_handle, I_INSTALL_TABLES)) == NULL) {
+    mm_log( (1,"DSO_open: dlsym didn't find '%s': %s.\n",I_INSTALL_TABLES,dlerror()) );
+    return NULL;
+  }
+
+  mm_log( (1,"Calling install_tables\n") );
+  f(&symbol_table,&UTIL_table);
+  mm_log( (1,"Call ok.\n") );
+
+  /* (*plugin_symtab)=&symbol_table;
+     (*plugin_utiltab)=&UTIL_table; */
+  
+  mm_log( (1,"DSO_open: going to dlsym '%s'\n", I_FUNCTION_LIST ));
+  if ( (function_list=(func_ptr *)dlsym(d_handle, I_FUNCTION_LIST)) == NULL) {
+    mm_log( (1,"DSO_open: dlsym didn't find '%s': %s.\n",I_FUNCTION_LIST,dlerror()) );
+    return NULL;
+  }
+  
+  if ( (dso_handle=(DSO_handle*)malloc(sizeof(DSO_handle))) == NULL) return NULL;
+  
+  dso_handle->handle=d_handle; /* needed to close again */
+  dso_handle->function_list=function_list;
+  if ( (dso_handle->filename=(char*)malloc(strlen(file))) == NULL) { free(dso_handle); return NULL; }
+  strcpy(dso_handle->filename,file);
+
+  mm_log( (1,"DSO_open <- 0x%X\n",dso_handle) );
+  return (void*)dso_handle;
+}
+
+undef_int
+DSO_close(void *ptr) {
+  DSO_handle *handle;
+  mm_log((1,"DSO_close(ptr 0x%X)\n",ptr));
+  handle=(DSO_handle*) ptr;
+  return !dlclose(handle->handle);
+}
+
+#endif
+
diff --git a/dynaload.h b/dynaload.h
new file mode 100644 (file)
index 0000000..2007b93
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _DYNALOAD_H_
+#define _DYNALOAD_H_
+
+#include "log.h"
+
+#if defined(OS_hpux)
+#include <dl.h>
+typedef shl_t minthandle_t;
+#elif defined(WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+typedef HMODULE minthandle_t;
+#undef WIN32_LEAN_AND_MEAN
+#else 
+#include <dlfcn.h>
+typedef void *minthandle_t; 
+#endif 
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "ppport.h"
+
+#include "ext.h"
+
+typedef struct {
+  minthandle_t handle;
+  char *filename;
+  func_ptr *function_list;
+} DSO_handle;
+
+typedef struct {
+  HV* hv;
+  char *key;
+  void *store;
+} UTIL_args;
+
+int getobj(void *hv_t,char *key,char *type,void **store);
+int getint(void *hv_t,char *key,int *store);
+int getdouble(void *hv_t,char *key,double *store);
+int getvoid(void *hv_t,char *key,void **store);
+
+
+void *DSO_open(char* file,char** evalstring);
+int DSO_close(void *);
+void DSO_call(DSO_handle *handle,int func_index,HV* hv);
+
+#ifdef __EMX__ /* OS/2 */
+# ifndef RTLD_LAZY
+#  define RTLD_LAZY 0
+# endif /* RTLD_LAZY */
+int dlclose(minthandle_t);
+#endif /* __EMX__ */
+
+#ifdef DLSYMUN
+
+#define I_EVALSTR "_evalstr"
+#define I_SYMBOL_TABLE "_symbol_table"
+#define I_UTIL_TABLE "_util_table"
+#define I_FUNCTION_LIST "_function_list"
+#define I_INSTALL_TABLES "_install_tables"
+
+#else 
+
+#define I_EVALSTR "evalstr"
+#define I_SYMBOL_TABLE "symbol_table"
+#define I_UTIL_TABLE "util_table"
+#define I_FUNCTION_LIST "function_list"
+#define I_INSTALL_TABLES "install_tables"
+
+#endif
+
+#endif /* _DYNALOAD_H_ */
+
diff --git a/dynfilt/Makefile.PL b/dynfilt/Makefile.PL
new file mode 100644 (file)
index 0000000..fe48c4a
--- /dev/null
@@ -0,0 +1,92 @@
+use Config;
+use ExtUtils::MakeMaker;
+$Verbose = 1;
+
+$lddl=$Config{"lddlflags"};
+
+@plugins= qw(dyntest dt2 mandelbrot flines);
+
+$libstr=join(' ',map { $_.'.$(SO)' } @plugins);
+
+#print $libstr,"\n";
+#print $objstr,"\n";
+#print MY::top_targets();
+
+WriteMakefile(
+    NAME      => 'Imager::plugins',
+    SKIP      => [qw(all dynamic static )],
+    clean     => {'FILES' => $libstr},
+);
+
+
+
+
+sub lddl_magic {
+  my $t;
+  $t=$lddl;
+  $t=~s/-bI:\$\(PERL_INC\)\/perl.exp//;
+  $t=~s/\$\(BASEEXT\)/$_[0]/;
+  return $t;
+}
+
+
+
+
+sub MY::top_targets {
+    use Config;
+if ($^O eq 'aix') {
+        '
+all :: dynamic
+
+dynamic ::       '.$libstr.(join("\n",map { qq{
+
+$_.\$(SO): $_\$(OBJ_EXT)
+       LD_RUN_PATH="\$(LD_RUN_PATH)" \$(LD) -o \$\@ }.lddl_magic($_).qq{ \$(OTHERLDFLAGS) $_\$(OBJ_EXT)
+
+} } @plugins)).'
+
+
+pure_all :: 
+       \$(NOOP)
+
+';
+
+}
+elsif ($^O =~ /win32/i && $Config{cc} =~ /cl/) {
+    my @libpth = grep /\S/, split /("[^"]*"|\S+)/,$Config{libpth};
+    
+        '
+all :: dynamic
+
+dynamic ::       '.$libstr.(join("\n",map { qq{
+
+$_.\$(SO): $_\$(OBJ_EXT)
+    \$(LD) /OUT:\$\@ }.lddl_magic($_).qq{ \$(OTHERLDFLAGS) $_\$(OBJ_EXT) $Config{libs} }.join(' ', map "/libpath:$_", @libpth).qq{
+
+} } @plugins)).'
+
+
+pure_all :: 
+       \$(NOOP)
+
+';
+
+} else {
+        '
+all :: dynamic
+
+dynamic ::       '.$libstr.(join("\n",map { qq{
+
+$_.\$(SO): $_\$(OBJ_EXT)
+       LD_RUN_PATH="\$(LD_RUN_PATH)" \$(LD) -o \$\@ \$(LDDLFLAGS) \$(OTHERLDFLAGS) $_\$(OBJ_EXT)
+
+} } @plugins)).'
+
+
+pure_all :: 
+       \$(NOOP)
+
+';
+
+}
+}
diff --git a/dynfilt/compile.txt b/dynfilt/compile.txt
new file mode 100644 (file)
index 0000000..e0e3e6a
--- /dev/null
@@ -0,0 +1,2 @@
+linux - solaris : gcc -o dyntest.so -fPIC -G dyntest.c
+AIX: LD_RUN_PATH="" ld -o dyntest.a -bhalt:4 -bM:SRE -bE:dyntest.exp -b noentry -lc  -L/usr/local/lib  dyntest.o
diff --git a/dynfilt/dt2.c b/dynfilt/dt2.c
new file mode 100644 (file)
index 0000000..9806cef
--- /dev/null
@@ -0,0 +1,54 @@
+#include "pluginst.h"
+#include <stdio.h>
+
+char evalstr[]="Plugin for creating html tables from images";
+
+/* input parameters
+   fname - file to add the html to.
+
+*/
+
+
+
+void
+html_art(void *INP) {
+  i_img *im;
+  i_color rcolor;
+  int i,x,y;
+  int info[4];
+  FILE *fp;
+  char *fname;
+
+  if ( !getSTR("fname",&fname) ) { fprintf(stderr,"Error: filename is missing\n"); return; } 
+  if ( !getOBJ("image","Imager::ImgRaw",&im) ) { fprintf(stderr,"Error: image is missing\n"); return; }
+  
+  printf("parameters: (im 0x%x,fname %s)\n",im,fname); 
+
+  printf("image info:\n size (%d,%d)\n channels (%d)\n channel mask (%d)\n bytes (%d)\n",im->xsize,im->ysize,im->channels,im->ch_mask,im->bytes); 
+
+  fp=fopen(fname,"ab+");
+  fprintf(fp,"<TABLE BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\">");
+  for(y=0;y<im->ysize;y+=2) {
+    fprintf(fp,"<TR>");
+     for(x=0;x<im->xsize;x++) {
+      i_gpix(im,x,y,&rcolor);
+      fprintf(fp,"<TD BGCOLOR=\"#%02X%02X%02X\">&nbsp;&nbsp;</TD>",rcolor.rgb.r,rcolor.rgb.g,rcolor.rgb.b);
+    }
+    fprintf(fp,"</TR>"); 
+  }
+  fprintf(fp,"</TABLE>");
+  fclose(fp);
+}
+
+func_ptr function_list[]={
+  {
+    "html_art",
+    html_art,
+    "callseq => ['image','fname'], \
+    callsub => sub { my %hsh=@_; DSO_call($DSO_handle,0,\\%hsh); } \
+    "
+  },
+  {NULL,NULL,NULL}};
+
+
+/* Remember to double backslash backslashes within Double quotes in C */
diff --git a/dynfilt/dyntest.c b/dynfilt/dyntest.c
new file mode 100644 (file)
index 0000000..e991644
--- /dev/null
@@ -0,0 +1,73 @@
+#include "pluginst.h"
+
+
+char evalstr[]="Description string of plugin dyntest - kind of like";
+
+void null_plug(void *ptr) { }
+
+/* Example dynamic filter - level stretch (linear) - note it only stretches and doesn't compress */
+
+/* input parameters
+   a: the current black
+   b: the current white
+   
+   0 <= a < b <= 255;
+
+   output pixel value calculated by: o=((i-a)*255)/(b-a);
+
+   note that since we do not have the needed functions to manipulate the data structures *** YET ***
+*/
+
+
+unsigned char
+static
+saturate(int in) {
+  if (in>255) { return 255; }
+  else if (in>0) return in;
+  return 0;
+}
+
+void lin_stretch(void *INP) {
+
+  int a, b;
+  i_img *im;
+  i_color rcolor;
+  int i,bytes,x,y;
+  int info[4];
+
+  if ( !getOBJ("image","Imager::ImgRaw",&im) ) { fprintf(stderr,"Error: image is missing\n"); }
+  if ( !getINT("a",&a) ) { fprintf(stderr,"Error: a is missing\n"); }
+  if ( !getINT("b",&b) ) { fprintf(stderr,"Error: b is missing\n"); }
+  
+  /*   fprintf(stderr,"parameters: (im 0x%x,a %d,b %d)\n",im,a,b);*/
+  bytes=im->bytes;
+
+  i_img_info(im,info); 
+  for(i=0;i<4;i++) { printf("%d: %d\n",i,info[i]); } 
+  printf("image info:\n size (%d,%d)\n channels (%d)\n channel mask (%d)\n bytes (%d)\n",im->xsize,im->ysize,im->channels,im->ch_mask,im->bytes); 
+
+  for(y=0;y<im->ysize;y++) for(x=0;x<im->xsize;x++) {
+    i_gpix(im,x,y,&rcolor);
+    for(i=0;i<im->channels;i++) rcolor.channel[i]=saturate((255*(rcolor.channel[i]-a))/(b-a));    
+    i_ppix(im,x,y,&rcolor);
+  }
+
+}
+
+func_ptr function_list[]={
+  {
+    "null_plug",
+    null_plug,
+    "callsub => sub { 1; }"
+  },{
+    "lin_stretch",
+    lin_stretch,
+    "callseq => ['image','a','b'], \
+    callsub => sub { my %hsh=@_; DSO_call($DSO_handle,1,\\%hsh); } \
+    "
+  },
+  {NULL,NULL,NULL}};
+
+
+/* Remember to double backslash backslashes within Double quotes in C */
+
diff --git a/dynfilt/flines.c b/dynfilt/flines.c
new file mode 100644 (file)
index 0000000..95d982e
--- /dev/null
@@ -0,0 +1,72 @@
+#include "pluginst.h"
+
+
+char evalstr[]="Fancy lines";
+
+/* input parameters
+   image is the image object.
+*/
+
+
+
+unsigned char
+static
+saturate(int in) {
+  if (in>255) { return 255; }
+  else if (in>0) return in;
+  return 0;
+}
+
+
+
+void
+flines(void *INP) {
+  i_img *im;
+  i_color vl;
+  int i,bytes,x,y;
+  int idx;
+  
+  if ( !getOBJ("image","Imager::ImgRaw",&im) ) {
+               fprintf(stderr,"Error: image is missing\n"); 
+               return;
+       }
+  
+  fprintf(stderr, "flines: parameters: (im 0x%x)\n",im);
+  fprintf(stderr, "flines: image info:\n size (%d,%d)\n channels (%d)\n channel mask (%d)\n bytes (%d)\n",im->xsize,im->ysize,im->channels,im->ch_mask,im->bytes); 
+
+  for(y = 0; y < im->ysize; y ++) {
+    for(x = 0; x < im->xsize; x ++ ) {
+      i_gpix(im,x,y,&vl); 
+                       if (!(y%2)) {
+                               float yf = y/(float)im->ysize;
+                               float mf = 1.2-0.8*yf;
+                               vl.rgb.r = saturate(vl.rgb.r*mf);
+                               vl.rgb.g = saturate(vl.rgb.g*mf);
+                               vl.rgb.b = saturate(vl.rgb.b*mf);
+                       } else {
+                               float yf = (im->ysize-y)/(float)im->ysize;
+                               float mf = 1.2-0.8*yf;
+                               vl.rgb.r = saturate(vl.rgb.r*mf);
+                               vl.rgb.g = saturate(vl.rgb.g*mf);
+                               vl.rgb.b = saturate(vl.rgb.b*mf);
+                       } 
+     i_ppix(im,x,y,&vl); 
+    }
+  }
+}
+
+
+
+func_ptr function_list[]={
+  {
+    "flines",
+    flines,
+    "callseq => ['image'], \
+    callsub => sub { my %hsh=@_; DSO_call($DSO_handle,0,\\%hsh); } \
+    "
+  },
+  {NULL,NULL,NULL}};
+
+
+/* Remember to double backslash backslashes within Double quotes in C */
+
diff --git a/dynfilt/mandelbrot.c b/dynfilt/mandelbrot.c
new file mode 100644 (file)
index 0000000..09d88fb
--- /dev/null
@@ -0,0 +1,103 @@
+#include "pluginst.h"
+
+
+char evalstr[]="Mandlebrot renderer";
+
+/* Example Mandlebrot generator */
+
+/* input parameters
+   image is the image object.
+*/
+
+
+#define MXITER 256
+
+static
+int
+mandel(float x, float y) {
+  float xn, yn;
+  float xo, yo;
+  float dist;
+  int iter = 1;
+  /*   Z(n+1) = Z(n) ^2 + c */
+
+  /* printf("(%.2f, %.2f) -> \n", x,y);   */
+
+  xo = x;
+  yo = y;
+
+  while( xo*xo+yo*yo <= 10 && iter < MXITER) {
+    xn = xo*xo-yo*yo + x;
+    yn = 2*xo*yo     + y;
+    xo=xn;
+    yo=yn;
+    iter++;
+  }
+  return (iter == MXITER)?0:iter;
+}
+
+
+
+void mandlebrot(void *INP) {
+
+  i_img *im;
+  i_color vl;
+  int i,bytes,x,y;
+  int idx;
+  
+  float xs, ys;
+  float div;
+
+  i_color icl[256];
+  srand(12235);
+  for(i=1;i<256; i++) {
+    icl[i].rgb.r = 100+(int) (155.0*rand()/(RAND_MAX+1.0));
+    icl[i].rgb.g = 100+(int) (155.0*rand()/(RAND_MAX+1.0));
+    icl[i].rgb.g = 100+(int) (155.0*rand()/(RAND_MAX+1.0));
+  }
+
+  icl[0].rgb.r = 0;
+  icl[0].rgb.g = 0;
+  icl[0].rgb.g = 0;
+    
+
+  
+  if ( !getOBJ("image","Imager::ImgRaw",&im) ) { fprintf(stderr,"Error: image is missing\n"); }
+  
+  fprintf(stderr,"mandlebrot: parameters: (im 0x%x)\n",im);
+  bytes=im->bytes;
+
+  fprintf(stderr, "mandlebrot: image info:\n size (%d,%d)\n channels (%d)\n channel mask (%d)\n bytes (%d)\n",im->xsize,im->ysize,im->channels,im->ch_mask,im->bytes); 
+  div = 2.5;
+
+  xs = 0.8*div;
+  ys = 0.5*div;
+  
+  div /= im->xsize;
+
+
+  fprintf(stderr, "Divider: %f \n", div);
+  for(y = 0; y < im->ysize; y ++) {
+    for(x = 0; x < im->xsize; x ++ ) {
+      idx = mandel(x*div-xs , y*div-ys);
+      idx = (idx>255)?255:idx;
+      i_ppix(im,x,y,&icl[idx]); 
+    }
+  }
+}
+
+
+
+func_ptr function_list[]={
+  {
+    "mandlebrot",
+    mandlebrot,
+    "callseq => ['image'], \
+    callsub => sub { my %hsh=@_; DSO_call($DSO_handle,0,\\%hsh); } \
+    "
+  },
+  {NULL,NULL,NULL}};
+
+
+/* Remember to double backslash backslashes within Double quotes in C */
+
diff --git a/dynfilt/pluginst.h b/dynfilt/pluginst.h
new file mode 100644 (file)
index 0000000..3ba9bf0
--- /dev/null
@@ -0,0 +1,17 @@
+#include "../plug.h"
+
+#ifdef _MSC_VER
+#define WIN32_EXPORT __declspec(dllexport)
+#else
+/* this may need to change for other Win32 compilers */
+#define WIN32_EXPORT
+#endif
+
+symbol_table_t *symbol_table;
+UTIL_table_t *util_table;
+
+void WIN32_EXPORT
+install_tables(symbol_table_t *s,UTIL_table_t *u) {
+  symbol_table=s;
+  util_table=u;
+}
diff --git a/errep.perl b/errep.perl
new file mode 100644 (file)
index 0000000..b4ae4cb
--- /dev/null
@@ -0,0 +1,96 @@
+
+
+use Config;
+
+@precommands=('uname -a','perl -V');
+@manpages=('dlopen','shl_load','dlsym','dlclose');
+@postcommands=map { "man $_ | col -bf | cat -s" } @manpages;
+
+print <<EOF;
+
+  This script will gather information about your system in order to
+  help debugging the problem compiling or testing Imager on your
+  system.
+
+  Make sure that you are in the same directory as errep.perl is when
+  running the script.  Also make sure that the environment variables
+  are the same as when running perl Makefile.PL
+
+  It issues: uname -a, perl -V and gets the %Config hash from the
+  build of the perl binary.  Then it tries to build and test the
+  module (but not install it).  It dumps out the test logs if there
+  are any.  It ends by dumping out some manpages.
+
+  All the results are saved to the file 'report.txt'
+
+  Continue [Y/n]?
+
+EOF
+
+$a=<STDIN>;
+chomp($a);
+die "Aborted!\n" if $a =~ /^n/i;
+
+print "Generating info about system\n";
+
+open OSTD, '>&STDOUT' or die $!;
+open STDOUT, '>report.txt' or die $!;
+open STDERR, '>&STDOUT' or die $!;
+
+rcomm('rm testout/*');
+rcomm(@precommands);
+rcomm("$^X Makefile.PL") || rcomm("make") || rcomm("make test");
+head("Logfiles from run");
+dumplogs();
+
+pconf();
+rcomm(@postcommands);
+
+sub pconf {
+    head("perl Config parameters");
+    for(sort keys %Config) {  print $_,"=>",(defined $Config{$_} ? $Config{$_} : '(undef)'),"\n"; }
+    print "\n";
+}
+
+
+sub rcomm {
+    my @commands=@_;
+    my ($comm,$R);
+    for $comm(@commands) {
+       print "Executing '$comm'\n";
+       print OSTD "Executing '$comm'\n";
+       $R=system($comm);
+       print "warning - rc=$R\n" if $R;
+       print "=====================\n\n";
+    }
+    return $R;
+}
+
+sub head {
+    my $h=shift;
+    print "=========================\n";
+    print $h;
+    print "\n=========================\n";
+}
+
+sub dumplogs {
+    opendir(DH,"testout") || die "Cannot open dir testout: $!\n";
+    my @fl=sort grep(/\.log$/,readdir(DH));
+
+    for my $f (@fl) {
+       print "::::::::::::::\ntestout/$f\n::::::::::::::\n";
+       open(FH,"testout/$f") || warn "Cannot open testout/$f: $!\n";
+       print while(<FH>);
+       close(FH);
+    }
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/ext.h b/ext.h
new file mode 100644 (file)
index 0000000..ebd9a75
--- /dev/null
+++ b/ext.h
@@ -0,0 +1,18 @@
+#include "image.h"
+
+/* structures for passing data between Imager-plugin and the Imager-module */
+
+typedef struct {
+  char *name;
+  void (*iptr)(void* ptr);
+  char *pcode;
+} func_ptr;
+
+
+typedef struct {
+  int (*getstr)(void *hv_t,char* key,char **store);
+  int (*getint)(void *hv_t,char *key,int *store);
+  int (*getdouble)(void *hv_t,char* key,double *store);
+  int (*getvoid)(void *hv_t,char* key,void **store);
+  int (*getobj)(void *hv_t,char* key,char* type,void **store);
+} UTIL_table_t;
diff --git a/feat.c b/feat.c
new file mode 100644 (file)
index 0000000..ea84839
--- /dev/null
+++ b/feat.c
@@ -0,0 +1,10 @@
+#include "feat.h"
+
+undef_int
+i_has_format(char *frmt) {
+  int rc,i;
+  rc=0;
+  i=0;
+  while(i_format_list[i] != NULL) if ( !strcmp(frmt,i_format_list[i++]) ) rc=1;
+  return(rc);
+}
diff --git a/feat.h b/feat.h
new file mode 100644 (file)
index 0000000..a8d50b1
--- /dev/null
+++ b/feat.h
@@ -0,0 +1,25 @@
+#include "image.h"
+
+static char *i_format_list[]={
+#ifdef HAVE_LIBJPEG
+  "jpeg",
+#endif
+#ifdef HAVE_LIBTIFF
+  "tiff",
+#endif
+#ifdef HAVE_LIBPNG
+  "png",
+#endif
+#ifdef HAVE_LIBGIF
+  "gif",
+#endif
+#ifdef HAVE_LIBT1
+  "t1",
+#endif
+#ifdef HAVE_LIBTT
+  "tt",
+#endif
+  "raw",
+  "pnm",
+  NULL};
+
diff --git a/filterlist.perl b/filterlist.perl
new file mode 100755 (executable)
index 0000000..69ee199
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/perl 
+
+use Imager;
+print "Filter          Arguments\n";
+for $filt (keys %Imager::filters) {
+    @callseq=@{$Imager::filters{$filt}{'callseq'}};
+    %defaults=%{$Imager::filters{$filt}{'defaults'}};
+    shift(@callseq);
+    @b=map { exists($defaults{$_}) ? $_.'('.$defaults{$_}.')' : $_ } @callseq;
+    $str=join(" ",@b);    
+    printf("%-15s %s\n",$filt,$str );
+}
diff --git a/filters.c b/filters.c
new file mode 100644 (file)
index 0000000..51d2642
--- /dev/null
+++ b/filters.c
@@ -0,0 +1,951 @@
+#include "image.h"
+#include <stdlib.h>
+#include <math.h>
+
+
+/*
+=head1 NAME
+
+filters.c - implements filters that operate on images
+
+=head1 SYNOPSIS
+
+  
+  i_contrast(im, 0.8);
+  i_hardinvert(im);
+  // and more
+
+=head1 DESCRIPTION
+
+filters.c implements basic filters for Imager.  These filters
+should be accessible from the filter interface as defined in 
+the pod for Imager.
+
+=head1 FUNCTION REFERENCE
+
+Some of these functions are internal.
+
+=over 4
+
+=cut
+*/
+
+
+
+
+
+
+
+/* 
+=item i_contrast(im, intensity)
+
+Scales the pixel values by the amount specified.
+
+  im        - image object
+  intensity - scalefactor
+
+=cut
+*/
+
+void
+i_contrast(i_img *im, float intensity) {
+  int x, y;
+  unsigned char ch;
+  unsigned int new_color;
+  i_color rcolor;
+  
+  mm_log((1,"i_contrast(im %p, intensity %f)\n", im, intensity));
+  
+  if(intensity < 0) return;
+  
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    i_gpix(im, x, y, &rcolor);
+      
+    for(ch = 0; ch < im->channels; ch++) {
+      new_color = (unsigned int) rcolor.channel[ch];
+      new_color *= intensity;
+       
+      if(new_color > 255) {
+       new_color = 255;
+      }
+      rcolor.channel[ch] = (unsigned char) new_color;
+    }
+    i_ppix(im, x, y, &rcolor);
+  }
+}
+
+
+/* 
+=item i_hardinvert(im)
+
+Inverts the pixel values of the input image.
+
+  im        - image object
+
+=cut
+*/
+
+void
+i_hardinvert(i_img *im) {
+  int x, y;
+  unsigned char ch;
+  
+  i_color rcolor;
+  
+    mm_log((1,"i_hardinvert(im %p)\n", im));
+
+  for(y = 0; y < im->ysize; y++) {
+    for(x = 0; x < im->xsize; x++) {
+      i_gpix(im, x, y, &rcolor);
+      
+      for(ch = 0; ch < im->channels; ch++) {
+       rcolor.channel[ch] = 255 - rcolor.channel[ch];
+      }
+      
+      i_ppix(im, x, y, &rcolor);
+    }
+  }  
+}
+
+
+
+/*
+=item i_noise(im, amount, type)
+
+Inverts the pixel values by the amount specified.
+
+  im     - image object
+  amount - deviation in pixel values
+  type   - noise individual for each channel if true
+
+=cut
+*/
+
+#ifdef _MSC_VER
+/* random() is non-ASCII, even if it is better than rand() */
+#define random() rand()
+#endif
+
+void
+i_noise(i_img *im, float amount, unsigned char type) {
+  int x, y;
+  unsigned char ch;
+  int new_color;
+  float damount = amount * 2;
+  i_color rcolor;
+  int color_inc = 0;
+  
+  mm_log((1,"i_noise(im %p, intensity %.2f\n", im, amount));
+  
+  if(amount < 0) return;
+  
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    i_gpix(im, x, y, &rcolor);
+    
+    if(type == 0) {
+      color_inc = (amount - (damount * ((float)random() / RAND_MAX)));
+    }
+    
+    for(ch = 0; ch < im->channels; ch++) {
+      new_color = (int) rcolor.channel[ch];
+      
+      if(type != 0) {
+       new_color += (amount - (damount * ((float)random() / RAND_MAX)));
+      } else {
+       new_color += color_inc;
+      }
+      
+      if(new_color < 0) {
+       new_color = 0;
+      }
+      if(new_color > 255) {
+       new_color = 255;
+      }
+      
+      rcolor.channel[ch] = (unsigned char) new_color;
+    }
+    
+    i_ppix(im, x, y, &rcolor);
+  }
+}
+
+
+/* 
+=item i_noise(im, amount, type)
+
+Inverts the pixel values by the amount specified.
+
+  im     - image object
+  amount - deviation in pixel values
+  type   - noise individual for each channel if true
+
+=cut
+*/
+
+
+/*
+=item i_applyimage(im, add_im, mode)
+
+Apply's an image to another image
+
+  im     - target image
+  add_im - image that is applied to target
+  mode   - what method is used in applying:
+
+  0   Normal   
+  1   Multiply
+  2   Screen
+  3   Overlay
+  4   Soft Light
+  5   Hard Light
+  6   Color dodge
+  7   Color Burn
+  8   Darker
+  9   Lighter
+  10  Add
+  11  Subtract
+  12  Difference
+  13  Exclusion
+       
+=cut
+*/
+
+void i_applyimage(i_img *im, i_img *add_im, unsigned char mode) {
+  int x, y;
+  int mx, my;
+
+  mm_log((1, "i_applyimage(im %p, add_im %p, mode %d", im, add_im, mode));
+  
+  mx = (add_im->xsize <= im->xsize) ? add_im->xsize : add_im->xsize;
+  my = (add_im->ysize <= im->ysize) ? add_im->ysize : add_im->ysize;
+  
+  for(x = 0; x < mx; x++) {            
+    for(y = 0; y < my; y++) {
+    }
+  }
+}
+
+
+/* 
+=item i_bumpmap(im, bump, channel, light_x, light_y, st)
+
+Makes a bumpmap on image im using the bump image as the elevation map.
+
+  im      - target image
+  bump    - image that contains the elevation info
+  channel - to take the elevation information from
+  light_x - x coordinate of light source
+  light_y - y coordinate of light source
+  st      - length of shadow
+
+=cut
+*/
+
+void
+i_bumpmap(i_img *im, i_img *bump, int channel, int light_x, int light_y, int st) {
+  int x, y, ch;
+  int mx, my;
+  i_color x1_color, y1_color, x2_color, y2_color, dst_color;
+  double nX, nY;
+  double tX, tY, tZ;
+  double aX, aY, aL;
+  double fZ;
+  unsigned char px1, px2, py1, py2;
+  
+  i_img new_im;
+
+  mm_log((1, "i_bumpmap(im %p, add_im %p, channel %d, light_x %d, light_y %d, st %d)\n",
+         im, bump, channel, light_x, light_y, st));
+
+
+  if(channel >= bump->channels) {
+    mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
+    return;
+  }
+  
+  mx = (bump->xsize <= im->xsize) ? bump->xsize : im->xsize;
+  my = (bump->ysize <= im->ysize) ? bump->ysize : im->ysize;
+
+  i_img_empty_ch(&new_im, im->xsize, im->ysize, im->channels);
+  aX = (light_x > (mx >> 1)) ? light_x : mx - light_x;
+  aY = (light_y > (my >> 1)) ? light_y : my - light_y;
+
+  aL = sqrt((aX * aX) + (aY * aY));
+
+  for(y = 1; y < my - 1; y++) {                
+    for(x = 1; x < mx - 1; x++) {
+      i_gpix(bump, x + st, y, &x1_color);
+      i_gpix(bump, x, y + st, &y1_color);
+      i_gpix(bump, x - st, y, &x2_color);
+      i_gpix(bump, x, y - st, &y2_color);
+
+      i_gpix(im, x, y, &dst_color);
+
+      px1 = x1_color.channel[channel];
+      py1 = y1_color.channel[channel];
+      px2 = x2_color.channel[channel];
+      py2 = y2_color.channel[channel];
+
+      nX = px1 - px2;
+      nY = py1 - py2;
+
+      nX += 128;
+      nY += 128;
+
+      fZ = (sqrt((nX * nX) + (nY * nY)) / aL);
+      tX = abs(x - light_x) / aL;
+      tY = abs(y - light_y) / aL;
+
+      tZ = 1 - (sqrt((tX * tX) + (tY * tY)) * fZ);
+      
+      if(tZ < 0) tZ = 0;
+      if(tZ > 2) tZ = 2;
+
+      for(ch = 0; ch < im->channels; ch++)
+       dst_color.channel[ch] = (unsigned char) (float)(dst_color.channel[ch] * tZ);
+      
+      i_ppix(&new_im, x, y, &dst_color);
+    }
+  }
+
+  i_copyto(im, &new_im, 0, 0, (int)im->xsize, (int)im->ysize, 0, 0);
+  
+  i_img_exorcise(&new_im);
+}
+
+
+
+/* 
+=item i_postlevels(im, levels)
+
+Quantizes Images to fewer levels.
+
+  im      - target image
+  levels  - number of levels
+
+=cut
+*/
+
+void
+i_postlevels(i_img *im, int levels) {
+  int x, y, ch;
+  float pv;
+  int rv;
+  float av;
+
+  i_color rcolor;
+
+  rv = (int) ((float)(256 / levels));
+  av = (float)levels;
+
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    i_gpix(im, x, y, &rcolor);
+
+    for(ch = 0; ch < im->channels; ch++) {
+      pv = (((float)rcolor.channel[ch] / 255)) * av;
+      pv = (int) ((int)pv * rv);
+
+      if(pv < 0) pv = 0;
+      else if(pv > 255) pv = 255;
+
+      rcolor.channel[ch] = (unsigned char) pv;
+    }
+    i_ppix(im, x, y, &rcolor);
+  }
+}
+
+
+/* 
+=item i_mosaic(im, size)
+
+Makes an image looks like a mosaic with tilesize of size
+
+  im      - target image
+  size    - size of tiles
+
+=cut
+*/
+
+void
+i_mosaic(i_img *im, int size) {
+  int x, y, ch;
+  int lx, ly, z;
+  long sqrsize;
+
+  i_color rcolor;
+  long col[256];
+  
+  sqrsize = size * size;
+  
+  for(y = 0; y < im->ysize; y += size) for(x = 0; x < im->xsize; x += size) {
+    for(z = 0; z < 256; z++) col[z] = 0;
+    
+    for(lx = 0; lx < size; lx++) {
+      for(ly = 0; ly < size; ly++) {
+       i_gpix(im, (x + lx), (y + ly), &rcolor);
+         
+       for(ch = 0; ch < im->channels; ch++) {
+         col[ch] += rcolor.channel[ch];
+       }
+      }
+    }
+      
+    for(ch = 0; ch < im->channels; ch++)
+      rcolor.channel[ch] = (int) ((float)col[ch] / sqrsize);
+    
+    
+    for(lx = 0; lx < size; lx++)
+      for(ly = 0; ly < size; ly++)
+      i_ppix(im, (x + lx), (y + ly), &rcolor);
+    
+  }
+}
+
+/*
+=item saturate(in) 
+
+Clamps the input value between 0 and 255. (internal)
+
+  in - input integer
+
+=cut
+*/
+
+static
+unsigned char
+saturate(int in) {
+  if (in>255) { return 255; }
+  else if (in>0) return in;
+  return 0;
+}
+
+
+/*
+=item i_watermark(im, wmark, tx, ty, pixdiff) 
+
+Applies a watermark to the target image
+
+  im      - target image
+  wmark   - watermark image
+  tx      - x coordinate of where watermark should be applied
+  ty      - y coordinate of where watermark should be applied
+  pixdiff - the magnitude of the watermark, controls how visible it is
+
+=cut
+*/
+
+void
+i_watermark(i_img *im, i_img *wmark, int tx, int ty, int pixdiff) {
+  int vx, vy, ch;
+  i_color val, wval;
+
+  for(vx=0;vx<128;vx++) for(vy=0;vy<110;vy++) {
+    
+    i_gpix(im,    tx+vx, ty+vy,&val );
+    i_gpix(wmark, vx,    vy,   &wval);
+    
+    for(ch=0;ch<im->channels;ch++) 
+      val.channel[ch] = saturate( val.channel[ch] + (pixdiff* (wval.channel[0]-128) )/128 );
+    
+    i_ppix(im,tx+vx,ty+vy,&val);
+  }
+}
+
+
+/*
+=item i_autolevels(im, lsat, usat, skew)
+
+Scales and translates each color such that it fills the range completely.
+Skew is not implemented yet - purpose is to control the color skew that can
+occur when changing the contrast.
+
+  im   - target image
+  lsat - fraction of pixels that will be truncated at the lower end of the spectrum
+  usat - fraction of pixels that will be truncated at the higher end of the spectrum
+  skew - not used yet
+
+=cut
+*/
+
+void
+i_autolevels(i_img *im, float lsat, float usat, float skew) {
+  i_color val;
+  int i, x, y, rhist[256], ghist[256], bhist[256];
+  int rsum, rmin, rmax;
+  int gsum, gmin, gmax;
+  int bsum, bmin, bmax;
+  int rcl, rcu, gcl, gcu, bcl, bcu;
+
+  mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew));
+
+  rsum=gsum=bsum=0;
+  for(i=0;i<256;i++) rhist[i]=ghist[i]=bhist[i] = 0;
+  /* create histogram for each channel */
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    i_gpix(im, x, y, &val);
+    rhist[val.channel[0]]++;
+    ghist[val.channel[1]]++;
+    bhist[val.channel[2]]++;
+  }
+
+  for(i=0;i<256;i++) {
+    rsum+=rhist[i];
+    gsum+=ghist[i];
+    bsum+=bhist[i];
+  }
+  
+  rmin = gmin = bmin = 0;
+  rmax = gmax = bmax = 255;
+  
+  rcu = rcl = gcu = gcl = bcu = bcl = 0;
+  
+  for(i=0; i<256; i++) { 
+    rcl += rhist[i];     if ( (rcl<rsum*lsat) ) rmin=i;
+    rcu += rhist[255-i]; if ( (rcu<rsum*usat) ) rmax=255-i;
+
+    gcl += ghist[i];     if ( (gcl<gsum*lsat) ) gmin=i;
+    gcu += ghist[255-i]; if ( (gcu<gsum*usat) ) gmax=255-i;
+
+    bcl += bhist[i];     if ( (bcl<bsum*lsat) ) bmin=i;
+    bcu += bhist[255-i]; if ( (bcu<bsum*usat) ) bmax=255-i;
+  }
+
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    i_gpix(im, x, y, &val);
+    val.channel[0]=saturate((val.channel[0]-rmin)*255/(rmax-rmin));
+    val.channel[1]=saturate((val.channel[1]-gmin)*255/(gmax-gmin));
+    val.channel[2]=saturate((val.channel[2]-bmin)*255/(bmax-bmin));
+    i_ppix(im, x, y, &val);
+  }
+}
+
+/*
+=item Noise(x,y)
+
+Pseudo noise utility function used to generate perlin noise. (internal)
+
+  x - x coordinate
+  y - y coordinate
+
+=cut
+*/
+
+static
+float
+Noise(int x, int y) {
+  int n = x + y * 57; 
+  n = (n<<13) ^ n;
+  return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
+}
+
+/*
+=item SmoothedNoise1(x,y)
+
+Pseudo noise utility function used to generate perlin noise. (internal)
+
+  x - x coordinate
+  y - y coordinate
+
+=cut
+*/
+
+static
+float
+SmoothedNoise1(float x, float y) {
+  float corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16;
+  float sides   = ( Noise(x-1, y)  +Noise(x+1, y)  +Noise(x, y-1)  +Noise(x, y+1) ) /  8;
+  float center  =  Noise(x, y) / 4;
+  return corners + sides + center;
+}
+
+
+/*
+=item G_Interpolate(a, b, x)
+
+Utility function used to generate perlin noise. (internal)
+
+=cut
+*/
+
+static
+float C_Interpolate(float a, float b, float x) {
+  /*  float ft = x * 3.1415927; */
+  float ft = x * PI;
+  float f = (1 - cos(ft)) * .5;
+  return  a*(1-f) + b*f;
+}
+
+
+/*
+=item InterpolatedNoise(x, y)
+
+Utility function used to generate perlin noise. (internal)
+
+=cut
+*/
+
+static
+float
+InterpolatedNoise(float x, float y) {
+
+  int integer_X = x;
+  float fractional_X = x - integer_X;
+  int integer_Y = y;
+  float fractional_Y = y - integer_Y;
+
+  float v1 = SmoothedNoise1(integer_X,     integer_Y);
+  float v2 = SmoothedNoise1(integer_X + 1, integer_Y);
+  float v3 = SmoothedNoise1(integer_X,     integer_Y + 1);
+  float v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1);
+
+  float i1 = C_Interpolate(v1 , v2 , fractional_X);
+  float i2 = C_Interpolate(v3 , v4 , fractional_X);
+
+  return C_Interpolate(i1 , i2 , fractional_Y);
+}
+
+
+
+/*
+=item PerlinNoise_2D(x, y)
+
+Utility function used to generate perlin noise. (internal)
+
+=cut
+*/
+
+static
+float
+PerlinNoise_2D(float x, float y) {
+  int i,frequency;
+  float amplitude;
+  float total = 0;
+  int Number_Of_Octaves=6;
+  int n = Number_Of_Octaves - 1;
+
+  for(i=0;i<n;i++) {
+    frequency = 2*i;
+    amplitude = PI;
+    total = total + InterpolatedNoise(x * frequency, y * frequency) * amplitude;
+  }
+
+  return total;
+}
+
+
+/*
+=item i_radnoise(im, xo, yo, rscale, ascale)
+
+Perlin-like radial noise.
+
+  im     - target image
+  xo     - x coordinate of center
+  yo     - y coordinate of center
+  rscale - radial scale
+  ascale - angular scale
+
+=cut
+*/
+
+void
+i_radnoise(i_img *im, int xo, int yo, float rscale, float ascale) {
+  int x, y, ch;
+  i_color val;
+  unsigned char v;
+  float xc, yc, r;
+  double a;
+  
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    xc = (float)x-xo+0.5;
+    yc = (float)y-yo+0.5;
+    r = rscale*sqrt(xc*xc+yc*yc)+1.2;
+    a = (PI+atan2(yc,xc))*ascale;
+    v = saturate(128+100*(PerlinNoise_2D(a,r)));
+    /* v=saturate(120+12*PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale));  Good soft marble */ 
+    for(ch=0; ch<im->channels; ch++) val.channel[ch]=v;
+    i_ppix(im, x, y, &val);
+  }
+}
+
+
+/*
+=item i_turbnoise(im, xo, yo, scale)
+
+Perlin-like 2d noise noise.
+
+  im     - target image
+  xo     - x coordinate translation
+  yo     - y coordinate translation
+  scale  - scale of noise
+
+=cut
+*/
+
+void
+i_turbnoise(i_img *im, float xo, float yo, float scale) {
+  int x,y,ch;
+  unsigned char v;
+  i_color val;
+
+  for(y = 0; y < im->ysize; y++) for(x = 0; x < im->xsize; x++) {
+    /*    v=saturate(125*(1.0+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))); */
+    v = saturate(120*(1.0+sin(xo+(float)x/scale+PerlinNoise_2D(xo+(float)x/scale,yo+(float)y/scale))));
+    for(ch=0; ch<im->channels; ch++) val.channel[ch] = v;
+    i_ppix(im, x, y, &val);
+  }
+}
+
+
+
+/*
+=item i_gradgen(im, num, xo, yo, ival, dmeasure)
+
+Gradient generating function.
+
+  im     - target image
+  num    - number of points given
+  xo     - array of x coordinates
+  yo     - array of y coordinates
+  ival   - array of i_color objects
+  dmeasure - distance measure to be used.  
+    0 = Euclidean
+    1 = Euclidean squared
+    2 = Manhattan distance
+
+=cut
+*/
+
+
+void
+i_gradgen(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
+  
+  i_color val;
+  int p, x, y, ch;
+  int channels = im->channels;
+  int xsize    = im->xsize;
+  int ysize    = im->ysize;
+
+  float *fdist;
+
+  mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
+  
+  for(p = 0; p<num; p++) {
+    mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
+    ICL_info(&ival[p]);
+  }
+
+  fdist = mymalloc( sizeof(float) * num );
+  
+  for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
+    float cs = 0;
+    float csd = 0;
+    for(p = 0; p<num; p++) {
+      int xd    = x-xo[p];
+      int yd    = y-yo[p];
+      switch (dmeasure) {
+      case 0: /* euclidean */
+       fdist[p]  = sqrt(xd*xd + yd*yd); /* euclidean distance */
+       break;
+      case 1: /* euclidean squared */
+       fdist[p]  = xd*xd + yd*yd; /* euclidean distance */
+       break;
+      case 2: /* euclidean squared */
+       fdist[p]  = max(xd*xd, yd*yd); /* manhattan distance */
+       break;
+      default:
+       m_fatal(3,"i_gradgen: Unknown distance measure\n");
+      }
+      cs += fdist[p];
+    }
+    
+    csd = 1/((num-1)*cs);
+
+    for(p = 0; p<num; p++) fdist[p] = (cs-fdist[p])*csd;
+    
+    for(ch = 0; ch<channels; ch++) {
+      int tres = 0;
+      for(p = 0; p<num; p++) tres += ival[p].channel[ch] * fdist[p];
+      val.channel[ch] = saturate(tres);
+    }
+    i_ppix(im, x, y, &val); 
+  }
+  
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void
+i_nearest_color_foo(i_img *im, int num, int *xo, int *yo, i_color *ival, int dmeasure) {
+
+  i_color val;
+  int p, x, y, ch;
+  int channels = im->channels;
+  int xsize    = im->xsize;
+  int ysize    = im->ysize;
+
+  mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
+  
+  for(p = 0; p<num; p++) {
+    mm_log((1,"i_gradgen: (%d, %d)\n", xo[p], yo[p]));
+    ICL_info(&ival[p]);
+  }
+
+  for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
+    int   midx    = 0;
+    float mindist = 0;
+    float curdist = 0;
+
+    int xd        = x-xo[0];
+    int yd        = y-yo[0];
+
+    switch (dmeasure) {
+    case 0: /* euclidean */
+      mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
+      break;
+    case 1: /* euclidean squared */
+      mindist = xd*xd + yd*yd; /* euclidean distance */
+      break;
+    case 2: /* euclidean squared */
+      mindist = max(xd*xd, yd*yd); /* manhattan distance */
+      break;
+    default:
+      m_fatal(3,"i_nearest_color: Unknown distance measure\n");
+    }
+
+    for(p = 1; p<num; p++) {
+      xd = x-xo[p];
+      yd = y-yo[p];
+      switch (dmeasure) {
+      case 0: /* euclidean */
+       curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
+       break;
+      case 1: /* euclidean squared */
+       curdist = xd*xd + yd*yd; /* euclidean distance */
+       break;
+      case 2: /* euclidean squared */
+       curdist = max(xd*xd, yd*yd); /* manhattan distance */
+       break;
+      default:
+       m_fatal(3,"i_nearest_color: Unknown distance measure\n");
+      }
+      if (curdist < mindist) {
+       mindist = curdist;
+       midx = p;
+      }
+    }
+    i_ppix(im, x, y, &ival[midx]); 
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
+void
+i_nearest_color(i_img *im, int num, int *xo, int *yo, i_color *oval, int dmeasure) {
+  i_color *ival;
+  float *tval;
+  float c1, c2;
+  i_color val;
+  int p, x, y, ch;
+  int channels = im->channels;
+  int xsize    = im->xsize;
+  int ysize    = im->ysize;
+  int *cmatch;
+
+  mm_log((1,"i_nearest_color(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
+  
+
+  tval   = mymalloc( sizeof(float)*num*im->channels );
+  ival   = mymalloc( sizeof(i_color)*num );
+  cmatch = mymalloc( sizeof(int)*num     );
+
+  for(p = 0; p<num; p++) {
+    for(ch = 0; ch<im->channels; ch++) tval[ p * im->channels + ch] = 0;
+    cmatch[p] = 0;
+  }
+
+  
+  for(y = 0; y<ysize; y++) for(x = 0; x<xsize; x++) {
+    int   midx    = 0;
+    float mindist = 0;
+    float curdist = 0;
+    
+    int xd        = x-xo[0];
+    int yd        = y-yo[0];
+
+    switch (dmeasure) {
+    case 0: /* euclidean */
+      mindist = sqrt(xd*xd + yd*yd); /* euclidean distance */
+      break;
+    case 1: /* euclidean squared */
+      mindist = xd*xd + yd*yd; /* euclidean distance */
+      break;
+    case 2: /* euclidean squared */
+      mindist = max(xd*xd, yd*yd); /* manhattan distance */
+      break;
+    default:
+      m_fatal(3,"i_nearest_color: Unknown distance measure\n");
+    }
+    
+    for(p = 1; p<num; p++) {
+      xd = x-xo[p];
+      yd = y-yo[p];
+      switch (dmeasure) {
+      case 0: /* euclidean */
+       curdist = sqrt(xd*xd + yd*yd); /* euclidean distance */
+       break;
+      case 1: /* euclidean squared */
+       curdist = xd*xd + yd*yd; /* euclidean distance */
+       break;
+      case 2: /* euclidean squared */
+       curdist = max(xd*xd, yd*yd); /* manhattan distance */
+       break;
+      default:
+       m_fatal(3,"i_nearest_color: Unknown distance measure\n");
+      }
+      if (curdist < mindist) {
+       mindist = curdist;
+       midx = p;
+      }
+    }
+
+    cmatch[midx]++;
+    i_gpix(im, x, y, &val);
+    c2 = 1.0/(float)(cmatch[midx]);
+    c1 = 1.0-c2;
+    
+    //    printf("pixel [%d %d %d] c1+c2 = %f\n", val.channel[0], val.channel[1], val.channel[2], c1+c2);
+    //    printf("cmatch = %d, c1 = %f, c2 = %f tval=[%f %f %f]\n", cmatch[midx], c1, c2, tval[midx*im->channels], tval[midx*im->channels+1], tval[midx*im->channels+2] );
+
+    for(ch = 0; ch<im->channels; ch++) 
+      tval[midx*im->channels + ch] = c1*tval[midx*im->channels + ch] + c2 * (float) val.channel[ch];
+    
+    
+  }
+
+  for(p = 0; p<num; p++) for(ch = 0; ch<im->channels; ch++) ival[p].channel[ch] = tval[p*im->channels + ch];
+
+  i_nearest_color_foo(im, num, xo, yo, ival, dmeasure);
+}
diff --git a/font.c b/font.c
new file mode 100644 (file)
index 0000000..5f11121
--- /dev/null
+++ b/font.c
@@ -0,0 +1,1176 @@
+#include "image.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+
+
+
+
+/*
+=head1 NAME
+
+font.c - implements font handling functions for t1 and truetype fonts
+
+=head1 SYNOPSIS
+
+  i_init_fonts();
+
+  #ifdef HAVE_LIBT1
+  fontnum = i_t1_new(path_to_pfb, path_to_afm);
+  i_t1_bbox(fontnum, points, "foo", 3, int cords[6]);
+  rc = i_t1_destroy(fontnum);
+  #endif
+
+  #ifdef HAVE_LIBTT
+  handle = i_tt_new(path_to_ttf);
+  rc = i_tt_bbox(handle, points, "foo", 3, int cords[6]);
+  i_tt_destroy(handle);
+
+  // and much more
+
+=head1 DESCRIPTION
+
+font.c implements font creation, rendering, bounding box functions and
+more for Imager.
+
+=head1 FUNCTION REFERENCE
+
+Some of these functions are internal.
+
+=over 4
+
+=cut
+
+*/
+
+
+
+
+
+
+
+
+
+/* 
+=item i_init_fonts()
+
+Initialize font rendering libraries if they are avaliable.
+
+=cut
+*/
+
+undef_int 
+i_init_fonts() {
+  mm_log((1,"Initializing fonts\n"));
+
+#ifdef HAVE_LIBT1
+  init_t1();
+#endif
+  
+#ifdef HAVE_LIBTT
+  init_tt();
+#endif
+
+  return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
+}
+
+
+
+
+#ifdef HAVE_LIBT1
+
+
+
+/* 
+=item i_init_t1()
+
+Initializes the t1lib font rendering engine.
+
+=cut
+*/
+
+undef_int
+init_t1() {
+  mm_log((1,"init_t1()\n"));
+  if ((T1_InitLib(LOGFILE|IGNORE_CONFIGFILE|IGNORE_FONTDATABASE) == NULL)){
+    mm_log((1,"Initialization of t1lib failed\n"));
+    return(1);
+  }
+  T1_SetLogLevel(T1LOG_DEBUG);
+  i_t1_set_aa(1); /* Default Antialias value */
+  return(0);
+}
+
+
+/* 
+=item i_close_t1()
+
+Shuts the t1lib font rendering engine down.
+
+  This it seems that this function is never used.
+
+=cut
+*/
+
+void
+i_close_t1() {
+  T1_CloseLib();
+}
+
+
+/*
+=item i_t1_new(pfb, afm)
+
+Loads the fonts with the given filenames, returns its font id
+
+ pfb -  path to pfb file for font
+ afm -  path to afm file for font
+
+=cut
+*/
+
+int
+i_t1_new(char *pfb,char *afm) {
+  int font_id;
+  mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL")));
+  font_id = T1_AddFont(pfb);
+  if (font_id<0) {
+    mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id));
+    return font_id;
+  }
+  
+  if (afm != NULL) {
+    mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm));
+    if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm));
+  }
+  return font_id;
+}
+
+/*
+=item i_t1_destroy(font_id)
+
+Frees resources for a t1 font with given font id.
+
+   font_id - number of the font to free
+
+=cut
+*/
+
+int
+i_t1_destroy(int font_id) {
+  mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
+  return T1_DeleteFont(font_id);
+}
+
+
+/*
+=item i_t1_set_aa(st)
+
+Sets the antialiasing level of the t1 library.
+
+   st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
+
+=cut
+*/
+
+void
+i_t1_set_aa(int st) {
+  int i;
+  unsigned long cst[17];
+  switch(st) {
+  case 0:
+    T1_AASetBitsPerPixel( 8 );
+    T1_AASetLevel( T1_AA_NONE );
+    T1_AANSetGrayValues( 0, 255 );
+    mm_log((1,"setting T1 antialias to none\n"));
+    break;
+  case 1:
+    T1_AASetBitsPerPixel( 8 );
+    T1_AASetLevel( T1_AA_LOW );
+    T1_AASetGrayValues( 0,65,127,191,255 );
+    mm_log((1,"setting T1 antialias to low\n"));
+    break;
+  case 2:
+    T1_AASetBitsPerPixel(8);
+    T1_AASetLevel(T1_AA_HIGH);
+    for(i=0;i<17;i++) cst[i]=(i*255)/16;
+    T1_AAHSetGrayValues( cst );
+    mm_log((1,"setting T1 antialias to high\n"));
+  }
+}
+
+
+/* 
+=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
+
+Interface to text rendering into a single channel in an image
+
+   im        pointer to image structure
+   xb        x coordinate of start of string
+   yb        y coordinate of start of string ( see align )
+   channel - destination channel
+   fontnum - t1 library font id
+   points  - number of points in fontheight
+   str     - string to render
+   len     - string length
+   align   - (0 - top of font glyph | 1 - baseline )
+
+=cut
+*/
+
+undef_int
+i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align) {
+  GLYPH *glyph;
+  int xsize,ysize,x,y;
+  i_color val;
+
+  unsigned int ch_mask_store;
+  
+  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
+
+  glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
+
+  mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
+  mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
+  mm_log((1," advanceX: %d  advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
+  mm_log((1,"bpp: %d\n",glyph->bpp));
+  
+  xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
+  ysize=glyph->metrics.ascent-glyph->metrics.descent;
+  
+  mm_log((1,"width: %d height: %d\n",xsize,ysize));
+
+  ch_mask_store=im->ch_mask;
+  im->ch_mask=1<<channel;
+
+  if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
+  
+  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
+    val.channel[channel]=glyph->bits[y*xsize+x];
+    i_ppix(im,x+xb,y+yb,&val);
+  }
+  
+  im->ch_mask=ch_mask_store;
+  return 1;
+}
+
+
+/*
+=item i_t1_bbox(handle, fontnum, points, str, len, cords)
+
+function to get a strings bounding box given the font id and sizes
+
+   handle  - pointer to font handle   
+   fontnum - t1 library font id
+   points  - number of points in fontheight
+   str     - string to measure
+   len     - string length
+   cords   - the bounding box (modified in place)
+
+=cut
+*/
+
+void
+i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6]) {
+  BBox bbox;
+  BBox gbbox;
+  
+  mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
+  T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
+  bbox = T1_GetStringBBox(fontnum,str,len,0,T1_KERNING);
+  gbbox = T1_GetFontBBox(fontnum);
+  
+  mm_log((1,"bbox: (%d,%d,%d,%d)\n",
+         (int)(bbox.llx*points/1000),
+         (int)(gbbox.lly*points/1000),
+         (int)(bbox.urx*points/1000),
+         (int)(gbbox.ury*points/1000),
+         (int)(bbox.lly*points/1000),
+         (int)(bbox.ury*points/1000) ));
+
+
+  cords[0]=((float)bbox.llx*points)/1000;
+  cords[2]=((float)bbox.urx*points)/1000;
+
+  cords[1]=((float)gbbox.lly*points)/1000;
+  cords[3]=((float)gbbox.ury*points)/1000;
+
+  cords[4]=((float)bbox.lly*points)/1000;
+  cords[5]=((float)bbox.ury*points)/1000;
+}
+
+
+/*
+=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
+
+Interface to text rendering in a single color onto an image
+
+   im      - pointer to image structure
+   xb      - x coordinate of start of string
+   yb      - y coordinate of start of string ( see align )
+   cl      - color to draw the text in
+   fontnum - t1 library font id
+   points  - number of points in fontheight
+   str     - char pointer to string to render
+   len     - string length
+   align   - (0 - top of font glyph | 1 - baseline )
+
+=cut
+*/
+
+undef_int
+i_t1_text(i_img *im,int xb,int yb,i_color *cl,int fontnum,float points,char* str,int len,int align) {
+  GLYPH *glyph;
+  int xsize,ysize,x,y,ch;
+  i_color val;
+  unsigned char c,i;
+
+  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
+
+  glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
+
+  mm_log((1,"metrics:  ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
+  mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
+  mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY));
+  mm_log((1,"bpp: %d\n",glyph->bpp));
+  
+  xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing;
+  ysize=glyph->metrics.ascent-glyph->metrics.descent;
+  
+  mm_log((1,"width: %d height: %d\n",xsize,ysize));
+
+  if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; }
+  
+  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
+    c=glyph->bits[y*xsize+x];
+    i=255-c;
+    i_gpix(im,x+xb,y+yb,&val);
+    for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
+    i_ppix(im,x+xb,y+yb,&val);
+  }
+  return 1;
+}
+
+
+#endif /* HAVE_LIBT1 */
+
+
+
+
+
+
+
+
+
+
+/* Truetype font support */
+
+#ifdef HAVE_LIBTT
+
+
+/* Defines */
+
+#define USTRCT(x) ((x).z)
+#define TT_VALID( handle )  ( ( handle ).z != NULL )
+
+
+/* Prototypes */
+
+static  int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
+static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
+static void i_tt_done_raster_map( TT_Raster_Map *bit );
+static void i_tt_clear_raster_map( TT_Raster_Map* bit );
+static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off );
+static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j );
+static void i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth );
+static void i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth );
+static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth );
+static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
+static  int i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth );
+static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] );
+
+
+/* static globals needed */
+
+static TT_Engine    engine;
+static int  LTT_dpi    = 72; /* FIXME: this ought to be a part of the call interface */
+static int  LTT_hinted = 1;  /* FIXME: this too */
+
+
+/*
+ * FreeType interface
+ */
+
+
+/*
+=item init_tt()
+
+Initializes the freetype font rendering engine
+
+=cut
+*/
+
+undef_int
+init_tt() {
+  TT_Error  error;
+  mm_log((1,"init_tt()\n"));
+  error = TT_Init_FreeType( &engine );
+  if ( error ){
+    mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
+    return(1);
+  }
+  return(0);
+}
+
+
+/* 
+=item i_tt_get_instance(handle, points, smooth)
+
+Finds a points+smooth instance or if one doesn't exist in the cache
+allocates room and returns its cache entry
+
+   fontname - path to the font to load
+   handle   - handle to the font.
+   points   - points of the requested font
+   smooth   - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+int
+i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
+  int i,idx;
+  TT_Error error;
+  
+  mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",handle,points,smooth));
+  
+  if (smooth == -1) { /* Smooth doesn't matter for this search */
+    for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points) {
+      mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
+      return i;
+    }
+    smooth=1; /* We will be adding a font - add it as smooth then */
+  } else { /* Smooth doesn't matter for this search */
+    for(i=0;i<TT_CHC;i++) if (handle->instanceh[i].ptsize==points && handle->instanceh[i].smooth==smooth) {
+      mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
+      return i;
+    }
+  }
+  
+  /* Found the instance in the cache - return the cache index */
+  
+  for(idx=0;idx<TT_CHC;idx++) if (!(handle->instanceh[idx].order)) break; /* find the lru item */
+
+  mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
+  mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",USTRCT(handle->instanceh[idx].instance) ));
+  
+  if ( USTRCT(handle->instanceh[idx].instance) ) {
+    mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
+    TT_Done_Instance( handle->instanceh[idx].instance ); /* Free instance if needed */
+  }
+  
+  /* create and initialize instance */
+  /* FIXME: probably a memory leak on fail */
+  
+  (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) || 
+         ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
+         ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
+  
+  if ( error ) {
+    mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
+    return -1;
+  }
+  
+  /* Now that the instance should the inplace we need to lower all of the
+     ru counts and put `this' one with the highest entry */
+  
+  for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
+
+  handle->instanceh[idx].order=TT_CHC-1;
+  handle->instanceh[idx].ptsize=points;
+  handle->instanceh[idx].smooth=smooth;
+  TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
+
+  /* Zero the memory for the glyph storage so they are not thought as cached if they haven't been cached
+     since this new font was loaded */
+
+  for(i=0;i<256;i++) USTRCT(handle->instanceh[idx].glyphs[i])=NULL;
+  
+  return idx;
+}
+
+
+/*
+=item i_tt_new(fontname)
+
+Creates a new font handle object, finds a character map and initialise the
+the font handle's cache
+
+   fontname - path to the font to load
+
+=cut
+*/
+
+TT_Fonthandle*
+i_tt_new(char *fontname) {
+  TT_Error error;
+  TT_Fonthandle *handle;
+  unsigned short i,n;
+  unsigned short platform,encoding;
+  
+  mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
+  
+  /* allocate memory for the structure */
+  
+  handle=mymalloc( sizeof(TT_Fonthandle) );
+
+  /* load the typeface */
+  error = TT_Open_Face( engine, fontname, &handle->face );
+  if ( error ) {
+    if ( error == TT_Err_Could_Not_Open_File ) mm_log((1, "Could not find/open %s.\n", fontname ))
+    else mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname, error ));
+    return NULL;
+  }
+  
+  TT_Get_Face_Properties( handle->face, &(handle->properties) );
+  /* First, look for a Unicode charmap */
+  
+  n = handle->properties.num_CharMaps;
+  USTRCT( handle->char_map )=NULL; /* Invalidate character map */
+  
+  for ( i = 0; i < n; i++ ) {
+    TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
+    if ( (platform == 3 && encoding == 1 ) || (platform == 0 && encoding == 0 ) ) {
+      TT_Get_CharMap( handle->face, i, &(handle->char_map) );
+      break;
+    }
+  }
+
+  /* Zero the pointsizes - and ordering */
+  
+  for(i=0;i<TT_CHC;i++) {
+    USTRCT(handle->instanceh[i].instance)=NULL;
+    handle->instanceh[i].order=i;
+    handle->instanceh[i].ptsize=0;
+    handle->instanceh[i].smooth=-1;
+  }
+
+  mm_log((1,"i_tt_new <- 0x%X\n",handle));
+  return handle;
+}
+
+
+
+/*
+ * raster map management
+ */
+
+/* 
+=item i_tt_init_raster_map(bit, width, height, smooth)
+
+Allocates internal memory for the bitmap as needed by the parameters (internal)
+                
+   bit    - bitmap to allocate into
+   width  - width of the bitmap
+   height - height of the bitmap
+   smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ) {
+
+  mm_log((1,"i_tt_init_raster_map( bit 08x%08X, width %d, height %d, smooth %d)\n", bit, width, height, smooth));
+  
+  bit->rows  = height;
+  bit->width = ( width + 3 ) & -4;
+  bit->flow  = TT_Flow_Down;
+  
+  if ( smooth ) {
+    bit->cols  = bit->width;
+    bit->size  = bit->rows * bit->width;
+  } else {
+    bit->cols  = ( bit->width + 7 ) / 8;    /* convert to # of bytes     */
+    bit->size  = bit->rows * bit->cols;     /* number of bytes in buffer */
+  }
+  
+  mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %d)\n", bit->width, bit->cols, bit->rows, bit->size ));
+
+  bit->bitmap = (void *) mymalloc( bit->size );
+  if ( !bit->bitmap ) m_fatal(0,"Not enough memory to allocate bitmap (%d)!\n",bit->size );
+}
+
+
+/*
+=item i_tt_clear_raster_map(bit)
+
+Frees the bitmap data and sets pointer to NULL (internal)
+                
+   bit - bitmap to free
+
+=cut
+*/
+
+static
+void
+i_tt_done_raster_map( TT_Raster_Map *bit ) {
+  myfree( bit->bitmap );
+  bit->bitmap = NULL;
+}
+
+
+/*
+=item i_tt_clear_raster_map(bit)
+
+Clears the specified bitmap (internal)
+                
+   bit - bitmap to zero
+
+=cut
+*/
+
+
+static
+void
+i_tt_clear_raster_map( TT_Raster_Map*  bit ) {
+  memset( bit->bitmap, 0, bit->size );
+}
+
+
+/* 
+=item i_tt_blit_or(dst, src, x_off, y_off)
+
+function that blits one raster map into another (internal)
+                
+   dst   - destination bitmap
+   src   - source bitmap
+   x_off - x offset into the destination bitmap
+   y_off - y offset into the destination bitmap
+
+=cut
+*/
+
+static
+void
+i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ) {
+  int  x,  y;
+  int  x1, x2, y1, y2;
+  char *s, *d;
+  
+  x1 = x_off < 0 ? -x_off : 0;
+  y1 = y_off < 0 ? -y_off : 0;
+  
+  x2 = (int)dst->cols - x_off;
+  if ( x2 > src->cols ) x2 = src->cols;
+  
+  y2 = (int)dst->rows - y_off;
+  if ( y2 > src->rows ) y2 = src->rows;
+
+  if ( x1 >= x2 ) return;
+
+  /* do the real work now */
+
+  for ( y = y1; y < y2; ++y ) {
+    s = ( (char*)src->bitmap ) + y * src->cols + x1;
+    d = ( (char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
+    
+    for ( x = x1; x < x2; ++x ) *d++ |= *s++;
+  }
+}
+
+
+/* 
+=item i_tt_get_glyph(handle, inst, j) 
+
+Function to see if a glyph exists and if so cache it (internal)
+                
+   handle - pointer to font handle
+   inst   - font instance
+   j      - charcode of glyph
+
+=cut
+*/
+
+static
+int
+i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned char j) { /* FIXME: Check if unsigned char is enough */
+  unsigned short load_flags, code;
+  TT_Error error;
+
+  mm_log((1, "i_tt_get_glyph(handle 0x%X, inst %d, j %d (%c))\n",handle,inst,j,j));
+  mm_log((1, "handle->instanceh[inst].glyphs[j]=0x%08X\n",handle->instanceh[inst].glyphs[j] ));
+
+  if ( TT_VALID(handle->instanceh[inst].glyphs[j]) ) {
+    mm_log((1,"i_tt_get_glyph: %d in cache\n",j));
+    return 1;
+  }
+  
+  /* Ok - it wasn't cached - try to get it in */
+  load_flags = TTLOAD_SCALE_GLYPH;
+  if ( LTT_hinted ) load_flags |= TTLOAD_HINT_GLYPH;
+  
+  if ( !TT_VALID(handle->char_map) ) {
+    code = (j - ' ' + 1) < 0 ? 0 : (j - ' ' + 1);
+    if ( code >= handle->properties.num_Glyphs ) code = 0;
+  } else code = TT_Char_Index( handle->char_map, j );
+  
+  if ( (error = TT_New_Glyph( handle->face, &handle->instanceh[inst].glyphs[j])) ) 
+    mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
+  if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[j], code, load_flags)) )
+    mm_log((1, "Cannot allocate and load glyph: error 0x%x.\n", error ));
+  
+  /* At this point the glyph should be allocated and loaded */
+  /* Next get the glyph metrics */
+  
+  error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j] );
+  mm_log((1, "TT_Get_Glyph_Metrics: error 0x%x.\n", error ));
+  return 1;
+}
+
+
+/* 
+=item i_tt_destroy(handle)
+
+Clears the data taken by a font including all cached data such as
+pixmaps and glyphs
+                
+   handle - pointer to font handle
+
+=cut
+*/
+
+void
+i_tt_destroy( TT_Fonthandle *handle) {
+  TT_Close_Face( handle->face );
+  
+  /* FIXME: Should these be freed automatically by the library? 
+
+  TT_Done_Instance( instance );
+  void
+    i_tt_done_glyphs( void ) {
+    int  i;
+
+    if ( !glyphs ) return;
+    
+    for ( i = 0; i < 256; ++i ) TT_Done_Glyph( glyphs[i] );
+    free( glyphs );
+    
+    glyphs = NULL;
+  }
+  */
+}
+
+
+/*
+ * FreeType Rendering functions
+ */
+
+
+/* 
+=item i_tt_render_glyph(handle, gmetrics, bit, smallbit, x_off, y_off, smooth)
+
+Renders a single glyph into the bit rastermap (internal)
+
+   handle   - pointer to font handle
+   gmetrics - the metrics for the glyph to be rendered
+   bit      - large bitmap that is the destination for the text
+   smallbit - small bitmap that is used only if smooth is true
+   x_off    - x offset of glyph
+   y_off    - y offset of glyph
+   smooth   - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int x_off, int y_off, int smooth ) {
+  
+  mm_log((1,"i_tt_render_glyph(glyph 0x0%X, gmetrics 0x0%X, bit 0x%X, small_bit 0x%X, x_off %d, y_off %d, smooth %d)\n",
+         USTRCT(glyph), gmetrics, bit, small_bit, x_off,y_off,smooth));
+  
+  if ( !smooth ) TT_Get_Glyph_Bitmap( glyph, bit, x_off * 64, y_off * 64);
+  else {
+    TT_F26Dot6 xmin, ymin, xmax, ymax;
+    
+    xmin =  gmetrics->bbox.xMin & -64;
+    ymin =  gmetrics->bbox.yMin & -64;
+    xmax = (gmetrics->bbox.xMax + 63) & -64;
+    ymax = (gmetrics->bbox.yMax + 63) & -64;
+    
+    i_tt_clear_raster_map( small_bit );
+    TT_Get_Glyph_Pixmap( glyph, small_bit, -xmin, -ymin );
+    i_tt_blit_or( bit, small_bit, xmin/64 + x_off, -ymin/64 - y_off );
+  }
+}
+
+
+/*
+=item i_tt_render_all_glyphs(handle, inst, bit, small_bit, cords, txt, len, smooth)
+
+calls i_tt_render_glyph to render each glyph into the bit rastermap (internal)
+
+   handle   - pointer to font handle
+   inst     - font instance
+   bit      - large bitmap that is the destination for the text
+   smallbit - small bitmap that is used only if smooth is true
+   txt      - string to render
+   len      - length of the string to render
+   smooth   - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, TT_Raster_Map *small_bit, int cords[6], char* txt, int len, int smooth ) {
+  unsigned char j;
+  int i;
+  TT_F26Dot6 x,y;
+  
+  mm_log((1,"i_tt_render_all_glyphs( handle 0x%X, inst %d, bit 0x%X, small_bit 0x%X, txt '%.*s', len %d, smooth %d)\n",
+         handle, inst, bit, small_bit, len, txt, len, smooth));
+  
+  /* 
+     y=-( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem )/(handle->properties.header->Units_Per_EM);
+  */
+
+  x=cords[0]; /* FIXME: If you font is antialiased this should be expanded by one to allow for aa expansion and the allocation too - do before passing here */
+  y=-cords[1];
+  
+  for ( i = 0; i < len; i++ ) {
+    j = txt[i];
+    if ( !i_tt_get_glyph(handle,inst,j) ) continue;
+    i_tt_render_glyph( handle->instanceh[inst].glyphs[j], &handle->instanceh[inst].gmetrics[j], bit, small_bit, x, y, smooth );
+    x += handle->instanceh[inst].gmetrics[j].advance / 64;
+  }
+}
+
+
+/*
+ * Functions to render rasters (single channel images) onto images
+ */
+
+/* 
+=item i_tt_dump_raster_map2(im, bit, xb, yb, cl, smooth)
+
+Function to dump a raster onto an image in color used by i_tt_text() (internal).
+
+   im     - image to dump raster on
+   bit    - bitmap that contains the text to be dumped to im
+   xb, yb - coordinates, left edge and baseline
+   cl     - color to use for text
+   smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, i_color *cl, int smooth ) {
+  char *bmap;
+  i_color val;
+  int c, i, ch, x, y;
+  mm_log((1,"i_tt_dump_raster_map2(im 0x%x, bit 0x%X, xb %d, yb %d, cl 0x%X)\n",im,bit,xb,yb,cl));
+  
+  bmap = (char *)bit->bitmap;
+
+  if ( smooth ) {
+
+    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+      c=(255*bmap[y*(bit->cols)+x])/4;
+      i=255-c;
+      i_gpix(im,x+xb,y+yb,&val);
+      for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
+      i_ppix(im,x+xb,y+yb,&val);
+    }
+
+  } else {
+
+    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+      c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
+      i=255-c;
+      i_gpix(im,x+xb,y+yb,&val);
+      for(ch=0;ch<im->channels;ch++) val.channel[ch]=(c*cl->channel[ch]+i*val.channel[ch])/255;
+      i_ppix(im,x+xb,y+yb,&val);
+    }
+
+  }
+}
+
+
+/*
+=item i_tt_dump_raster_map_channel(im, bit, xb, yb, channel, smooth)
+
+Function to dump a raster onto a single channel image in color (internal)
+
+   im      - image to dump raster on
+   bit     - bitmap that contains the text to be dumped to im
+   xb, yb  - coordinates, left edge and baseline
+   channel - channel to copy to
+   smooth  - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+void
+i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map*  bit, int xb, int yb, int channel, int smooth ) {
+  char *bmap;
+  i_color val;
+  int c,x,y;
+
+  mm_log((1,"i_tt_dump_raster_channel(im 0x%x, bit 0x%X, xb %d, yb %d, channel %d)\n",im,bit,xb,yb,channel));
+  
+  bmap = (char *)bit->bitmap;
+  
+  if ( smooth ) {
+    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+      c=(255*bmap[y*(bit->cols)+x])/4;
+      i_gpix(im,x+xb,y+yb,&val);
+      val.channel[channel]=c;
+      i_ppix(im,x+xb,y+yb,&val);
+    }
+  } else {
+    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+      c=( bmap[y*bit->cols+x/8] & (128>>(x%8)) ) ? 255 : 0;
+      i_gpix(im,x+xb,y+yb,&val);
+      val.channel[channel]=c;
+      i_ppix(im,x+xb,y+yb,&val);
+    }
+  }
+}
+
+
+/* 
+=item i_tt_rasterize(handle, bit, cords, points, txt, len, smooth) 
+
+interface for generating single channel raster of text (internal)
+
+   handle - pointer to font handle
+   bit    - the bitmap that is allocated, rendered into and NOT freed
+   cords  - the bounding box (modified in place)
+   points - font size to use
+   txt    - string to render
+   len    - length of the string to render
+   smooth - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+static
+int
+i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char* txt, int len, int smooth ) {
+  int inst;
+  int width, height;
+  TT_Raster_Map small_bit;
+  
+  /* find or install an instance */
+  if ( (inst=i_tt_get_instance(handle,points,smooth)) < 0) { 
+    mm_log((1,"i_tt_rasterize: get instance failed\n"));
+    return 0;
+  }
+  
+  /* calculate bounding box */
+  i_tt_bbox_inst( handle, inst, txt, len, cords );
+  
+  width  = cords[2]-cords[0];
+  height = cords[3]-cords[1];
+  
+  mm_log((1,"i_tt_rasterize: width=%d, height=%d\n",width, height )); 
+  
+  i_tt_init_raster_map ( bit, width, height, smooth );
+  i_tt_clear_raster_map( bit );
+  if ( smooth ) i_tt_init_raster_map( &small_bit, handle->instanceh[inst].imetrics.x_ppem + 32, height, smooth );
+  
+  i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, smooth );
+
+  /*  ascent = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / handle->properties.header->Units_Per_EM; */
+  
+  if ( smooth ) i_tt_done_raster_map( &small_bit );
+  return 1;
+}
+
+
+
+/* 
+ * Exported text rendering interfaces
+ */
+
+
+/*
+=item i_tt_cp(handle, im, xb, yb, channel, points, txt, len, smooth)
+
+Interface to text rendering into a single channel in an image
+
+   handle  - pointer to font handle
+   im      - image to render text on to
+   xb, yb  - coordinates, left edge and baseline
+   channel - channel to render into
+   points  - font size to use
+   txt     - string to render
+   len     - length of the string to render
+   smooth  - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+undef_int
+i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char* txt, int len, int smooth ) {
+
+  int cords[6];
+  int ascent, st_offset;
+  TT_Raster_Map bit;
+  
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
+  
+  ascent=cords[3];
+  st_offset=cords[0];
+
+  i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , yb-ascent, channel, smooth );
+  i_tt_done_raster_map( &bit );
+
+  return 1;
+}
+
+
+/* 
+=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth) 
+
+Interface to text rendering in a single color onto an image
+
+   handle  - pointer to font handle
+   im      - image to render text on to
+   xb, yb  - coordinates, left edge and baseline
+   cl      - color to use for text
+   points  - font size to use
+   txt     - string to render
+   len     - length of the string to render
+   smooth  - boolean (True: antialias on, False: antialias is off)
+
+=cut
+*/
+
+undef_int
+i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char* txt, int len, int smooth) {
+  int cords[6];
+  int ascent, st_offset;
+  TT_Raster_Map bit;
+  
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth ) ) return 0;
+  
+  ascent=cords[3];
+  st_offset=cords[0];
+
+  i_tt_dump_raster_map2( im, &bit, xb-st_offset, yb-ascent, cl, smooth ); 
+  i_tt_done_raster_map( &bit );
+
+  return 1;
+}
+
+
+/*
+=item i_tt_bbox_inst(handle, inst, txt, len, cords) 
+
+Function to get texts bounding boxes given the instance of the font (internal)
+
+   handle - pointer to font handle
+   inst   -  font instance
+   txt    -  string to measure
+   len    -  length of the string to render
+   cords  - the bounding box (modified in place)
+
+=cut
+*/
+
+static
+undef_int
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6] ) {
+  int i, upm, ascent, descent, gascent, gdescent, width, casc, cdesc, first, start;
+  unsigned int j;
+  unsigned char *ustr;
+  ustr=(unsigned char*)txt;
+  
+  mm_log((1,"i_tt_box_inst(handle 0x%X,inst %d,txt '%.*s', len %d)\n",handle,inst,len,txt,len));
+
+  upm     = handle->properties.header->Units_Per_EM;
+  gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem ) / upm;
+  gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem ) / upm;
+  
+  width   = 0;
+  start   = 0;
+  
+  mm_log((1, "i_tt_box_inst: glyph='%c' ascent=%d descent=%d\n", j, ascent, descent));
+
+  first=1;
+  for ( i = 0; i < len; ++i ) {
+    j = ustr[i];
+    if ( i_tt_get_glyph(handle,inst,j) ) {
+      width += handle->instanceh[inst].gmetrics[j].advance   / 64;
+      casc   = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
+      cdesc  = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
+
+      mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", j, casc, cdesc));
+
+      if (first) {
+       start    = handle->instanceh[inst].gmetrics[j].bbox.xMin / 64;
+       ascent   = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
+       descent  = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
+       first = 0;
+      }
+
+      ascent  = (ascent  >  casc ?  ascent : casc );
+      descent = (descent < cdesc ? descent : cdesc);
+    }
+  }
+  
+  cords[0]=start;
+  cords[1]=gdescent;
+  cords[2]=width+start;
+  cords[3]=gascent;
+  cords[4]=descent;
+  cords[5]=ascent;
+  return 1;
+}
+
+
+/*
+=item i_tt_bbox(handle, points, txt, len, cords)
+
+Interface to get a strings bounding box
+
+   handle - pointer to font handle
+   points - font size to use
+   txt    - string to render
+   len    - length of the string to render
+   cords  - the bounding box (modified in place)
+
+=cut
+*/
+
+undef_int
+i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6]) {
+  int inst;
+  
+  mm_log((1,"i_tt_box(handle 0x%X,points %f,txt '%.*s', len %d)\n",handle,points,len,txt,len));
+
+  if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
+    mm_log((1,"i_tt_text: get instance failed\n"));
+    return 0;
+  }
+
+  return i_tt_bbox_inst(handle, inst, txt, len, cords);
+}
+
+
+
+#endif /* HAVE_LIBTT */
diff --git a/fontfiles/dcr10.afm b/fontfiles/dcr10.afm
new file mode 100644 (file)
index 0000000..e1eb535
--- /dev/null
@@ -0,0 +1,751 @@
+StartFontMetrics 2.0
+FontName dcr10
+FullName dcr10
+FamilyName dcr10
+Weight Medium
+ItalicAngle 0.000000
+IsFixedPitch false
+UnderlinePosition -133
+UnderlineThickness 20
+Version 1.0\(Level-B\)
+FontBBox -55, -250, 1054, 913
+Notice Copyright \(C\) 1994, Basil K. Malyshev. All Rights Reserved.\nBaKoMa Fonts Collection, Level-B.
+EncodingScheme FontSpecific
+CapHeight 683
+XHeight 430
+Descender -194
+Ascender 694
+StartCharMetrics 256
+C 0 ; WX 499.89 ; N grave ; B 107 508 294 698 ;
+C 1 ; WX 499.89 ; N acute ; B 205 508 392 698 ;
+C 2 ; WX 499.89 ; N circumflex ; B 115 537 384 694 ;
+C 3 ; WX 499.89 ; N tilde ; B 83 572 416 668 ;
+C 4 ; WX 499.89 ; N dieresis ; B 101 565 398 669 ;
+C 5 ; WX 499.89 ; N hungarumlaut ; B 126 511 420 699 ;
+C 6 ; WX 749.83 ; N ring ; B 263 540 458 716 ;
+C 7 ; WX 499.89 ; N caron ; B 117 515 382 640 ;
+C 8 ; WX 499.89 ; N breve ; B 98 519 401 694 ;
+C 9 ; WX 499.89 ; N macron ; B 69 555 430 590 ;
+C 10 ; WX 277.72 ; N dotaccent ; B 84 559 194 669 ;
+C 11 ; WX 444.33 ; N cedilla ; B 129 -205 369 -22 ;
+C 12 ; WX 277.72 ; N ogonek ; B 9 -194 226 20 ;
+C 13 ; WX 277.72 ; N quotesinglbase ; B 112 -194 233 110 ;
+C 14 ; WX 444.33 ; N guilsinglleft ; B 94 26 330 457 ;
+C 15 ; WX 444.33 ; N guilsinglright ; B 94 26 360 457 ;
+C 16 ; WX 499.89 ; N quotedblleft ; B 148 390 466 694 ;
+C 17 ; WX 499.89 ; N quotedblright ; B 33 390 351 694 ;
+C 18 ; WX 499.89 ; N quotedblbase ; B 138 -194 456 110 ;
+C 19 ; WX 666.5 ; N guillemotleft ; B 94 26 553 457 ;
+C 20 ; WX 666.5 ; N guillemotright ; B 94 26 583 457 ;
+C 21 ; WX 499.89 ; N endash ; B 0 253 499 279 ; L hyphen emdash ;
+C 22 ; WX 999.78 ; N emdash ; B 0 253 999 279 ;
+C 23 ; WX 0 ; N compoundwordmark ; B 0 0 0 0 ;
+C 24 ; WX 388.78 ; N perthousand ; B 55 -56 333 347 ;
+C 25 ; WX 277.72 ; N dotlessi ; B 25 0 243 441 ;
+C 26 ; WX 305.5 ; N dotlessj ; B -55 -205 201 441 ;
+C 27 ; WX 583.17 ; N ff ; B 26 0 632 705 ; L i ffi ; L l ffl ;
+C 28 ; WX 555.44 ; N fi ; B 26 0 528 705 ;
+C 29 ; WX 555.44 ; N fl ; B 26 0 528 705 ;
+C 30 ; WX 833.11 ; N ffi ; B 26 0 807 705 ;
+C 31 ; WX 833.11 ; N ffl ; B 26 0 807 705 ;
+C 32 ; WX 499.89 ; N visiblespace ; B 42 -107 457 252 ;
+C 33 ; WX 277.72 ; N exclam ; B 84 0 194 716 ;
+C 34 ; WX 499.89 ; N quotedbl ; B 117 349 383 711 ;
+C 35 ; WX 833.11 ; N numbersign ; B 56 -194 776 694 ;
+C 36 ; WX 499.89 ; N dollar ; B 57 -56 444 750 ;
+C 37 ; WX 833.11 ; N percent ; B 56 -56 776 750 ;
+C 38 ; WX 777.61 ; N ampersand ; B 42 -22 727 716 ;
+C 39 ; WX 277.72 ; N quoteright ; B 84 390 208 694 ; L quoteright quotedblright ;
+C 40 ; WX 388.78 ; N parenleft ; B 97 -250 332 750 ;
+C 41 ; WX 388.78 ; N parenright ; B 56 -250 291 750 ;
+C 42 ; WX 499.89 ; N asterisk ; B 65 320 434 750 ;
+C 43 ; WX 777.61 ; N plus ; B 56 -83 721 583 ;
+C 44 ; WX 277.72 ; N comma ; B 84 -194 205 110 ; L comma quotedblbase ;
+C 45 ; WX 333.28 ; N hyphen ; B 11 184 276 246 ; L hyphen endash ;
+C 46 ; WX 277.72 ; N period ; B 84 0 194 110 ;
+C 47 ; WX 499.89 ; N slash ; B 56 -250 443 750 ;
+C 48 ; WX 499.89 ; N zero ; B 39 -22 460 666 ;
+C 49 ; WX 499.89 ; N one ; B 87 0 421 666 ;
+C 50 ; WX 499.89 ; N two ; B 50 0 449 666 ;
+C 51 ; WX 499.89 ; N three ; B 42 -22 457 666 ;
+C 52 ; WX 499.89 ; N four ; B 28 0 471 677 ;
+C 53 ; WX 499.89 ; N five ; B 50 -22 449 666 ;
+C 54 ; WX 499.89 ; N six ; B 42 -22 457 666 ;
+C 55 ; WX 499.89 ; N seven ; B 56 -22 485 676 ;
+C 56 ; WX 499.89 ; N eight ; B 42 -22 457 666 ;
+C 57 ; WX 499.89 ; N nine ; B 42 -22 457 666 ;
+C 58 ; WX 277.72 ; N colon ; B 84 0 194 430 ;
+C 59 ; WX 277.72 ; N semicolon ; B 84 -194 197 430 ;
+C 60 ; WX 777.61 ; N less ; B 83 -33 694 533 ; L less guillemotleft ;
+C 61 ; WX 777.61 ; N equal ; B 56 140 721 360 ;
+C 62 ; WX 777.61 ; N greater ; B 83 -33 694 533 ; L greater guillemotright ;
+C 63 ; WX 472.11 ; N question ; B 56 0 415 705 ;
+C 64 ; WX 777.61 ; N at ; B 56 -11 721 705 ;
+C 65 ; WX 749.83 ; N A ; B 18 0 703 716 ;
+C 66 ; WX 708.17 ; N B ; B 20 0 637 683 ;
+C 67 ; WX 722 ; N C ; B 56 -22 665 705 ;
+C 68 ; WX 763.72 ; N D ; B 19 0 693 683 ;
+C 69 ; WX 680.39 ; N E ; B 17 0 637 680 ;
+C 70 ; WX 652.61 ; N F ; B 17 0 596 680 ;
+C 71 ; WX 784.56 ; N G ; B 56 -22 735 705 ;
+C 72 ; WX 749.83 ; N H ; B 17 0 704 683 ;
+C 73 ; WX 361 ; N I ; B 12 0 321 683 ;
+C 74 ; WX 513.78 ; N J ; B 37 -22 465 683 ;
+C 75 ; WX 777.61 ; N K ; B 17 0 722 683 ;
+C 76 ; WX 624.83 ; N L ; B 17 0 568 683 ;
+C 77 ; WX 916.44 ; N M ; B 21 0 866 683 ;
+C 78 ; WX 749.83 ; N N ; B 17 0 704 683 ;
+C 79 ; WX 777.61 ; N O ; B 56 -22 721 705 ;
+C 80 ; WX 680.39 ; N P ; B 19 0 609 683 ;
+C 81 ; WX 777.61 ; N Q ; B 56 -194 727 705 ;
+C 82 ; WX 735.94 ; N R ; B 19 -22 718 683 ;
+C 83 ; WX 555.44 ; N S ; B 56 -22 498 705 ;
+C 84 ; WX 722 ; N T ; B 37 0 686 677 ;
+C 85 ; WX 749.83 ; N U ; B 17 -22 704 683 ;
+C 86 ; WX 749.83 ; N V ; B 5 -22 716 683 ;
+C 87 ; WX 1027.56 ; N W ; B 4 -22 995 683 ;
+C 88 ; WX 749.83 ; N X ; B 9 0 712 683 ;
+C 89 ; WX 749.83 ; N Y ; B -3 0 724 683 ;
+C 90 ; WX 611 ; N Z ; B 56 0 560 683 ;
+C 91 ; WX 277.72 ; N bracketleft ; B 103 -250 255 750 ;
+C 92 ; WX 499.89 ; N backslash ; B 56 -250 443 750 ;
+C 93 ; WX 277.72 ; N bracketright ; B 22 -250 174 750 ;
+C 94 ; WX 611 ; N asciicircum ; B 117 461 493 628 ;
+C 95 ; WX 777.61 ; N underscore ; B 83 -233 694 -193 ;
+C 96 ; WX 277.72 ; N quoteleft ; B 70 390 194 694 ; L quoteleft quotedblleft ;
+C 97 ; WX 499.89 ; N a ; B 40 -11 493 447 ;
+C 98 ; WX 555.44 ; N b ; B 26 -11 521 694 ;
+C 99 ; WX 444.33 ; N c ; B 33 -11 415 447 ;
+C 100 ; WX 555.44 ; N d ; B 33 -11 528 694 ;
+C 101 ; WX 444.33 ; N e ; B 28 -11 415 447 ;
+C 102 ; WX 305.5 ; N f ; B 26 0 354 705 ; L i fi ; L f ff ; L l fl ;
+C 103 ; WX 499.89 ; N g ; B 28 -206 485 452 ;
+C 104 ; WX 555.44 ; N h ; B 24 0 530 694 ;
+C 105 ; WX 277.72 ; N i ; B 25 0 243 669 ;
+C 106 ; WX 305.5 ; N j ; B -55 -205 201 669 ;
+C 107 ; WX 527.67 ; N k ; B 26 0 511 694 ;
+C 108 ; WX 277.72 ; N l ; B 25 0 251 694 ;
+C 109 ; WX 833.11 ; N m ; B 24 0 810 441 ;
+C 110 ; WX 555.44 ; N n ; B 24 0 530 441 ;
+C 111 ; WX 499.89 ; N o ; B 28 -11 471 447 ;
+C 112 ; WX 555.44 ; N p ; B 26 -194 521 441 ;
+C 113 ; WX 527.67 ; N q ; B 33 -194 529 441 ;
+C 114 ; WX 391.56 ; N r ; B 26 0 364 441 ;
+C 115 ; WX 394.33 ; N s ; B 33 -11 360 447 ;
+C 116 ; WX 388.78 ; N t ; B 19 -11 332 615 ;
+C 117 ; WX 555.44 ; N u ; B 24 -11 530 441 ;
+C 118 ; WX 527.67 ; N v ; B 19 -11 508 430 ;
+C 119 ; WX 722 ; N w ; B 18 -11 703 430 ;
+C 120 ; WX 527.67 ; N x ; B 12 0 516 430 ;
+C 121 ; WX 527.67 ; N y ; B 19 -205 508 430 ;
+C 122 ; WX 444.33 ; N z ; B 28 0 401 430 ;
+C 123 ; WX 499.89 ; N braceleft ; B 70 -250 429 750 ;
+C 124 ; WX 277.72 ; N bar ; B 119 -250 159 750 ;
+C 125 ; WX 499.89 ; N braceright ; B 70 -250 429 750 ;
+C 126 ; WX 611 ; N asciitilde ; B 83 572 527 668 ;
+C 127 ; WX 333.28 ; N hyphen ; B 11 184 276 246 ;
+C 128 ; WX 749.83 ; N Abreve ; B 18 0 703 891 ;
+C 129 ; WX 749.83 ; N Aogonek ; B 18 -188 740 716 ;
+C 130 ; WX 722 ; N Cacute ; B 56 -22 665 895 ;
+C 131 ; WX 722 ; N Ccaron ; B 56 -22 665 856 ;
+C 132 ; WX 763.72 ; N Dcaron ; B 19 0 693 856 ;
+C 133 ; WX 680.39 ; N Ecaron ; B 17 0 637 856 ;
+C 134 ; WX 680.39 ; N Eogonek ; B 17 -188 637 680 ;
+C 135 ; WX 784.56 ; N Gbreve ; B 56 -22 735 891 ;
+C 136 ; WX 624.83 ; N Lacute ; B 17 0 568 895 ;
+C 137 ; WX 624.83 ; N Lquoteright ; B 17 0 568 683 ;
+C 138 ; WX 624.83 ; N Lslash ; B 17 0 568 683 ;
+C 139 ; WX 749.83 ; N Nacute ; B 17 0 704 895 ;
+C 140 ; WX 749.83 ; N Ncaron ; B 17 0 704 856 ;
+C 141 ; WX 727.61 ; N Eng ; B 24 -22 676 694 ;
+C 142 ; WX 777.61 ; N Ohungarumlaut ; B 56 -22 721 895 ;
+C 143 ; WX 735.94 ; N Racute ; B 19 -22 718 895 ;
+C 144 ; WX 735.94 ; N Rcaron ; B 19 -22 718 856 ;
+C 145 ; WX 555.44 ; N Sacute ; B 56 -22 498 895 ;
+C 146 ; WX 555.44 ; N Scaron ; B 56 -22 498 856 ;
+C 147 ; WX 555.44 ; N Scedilla ; B 56 -205 498 705 ;
+C 148 ; WX 722 ; N Tcaron ; B 37 0 686 856 ;
+C 149 ; WX 722 ; N Tcedilla ; B 37 -205 686 677 ;
+C 150 ; WX 749.83 ; N Uhungarumlaut ; B 17 -22 704 895 ;
+C 151 ; WX 749.83 ; N Uring ; B 17 -22 704 913 ;
+C 152 ; WX 749.83 ; N Ydieresis ; B -3 0 724 839 ;
+C 153 ; WX 611 ; N Zacute ; B 56 0 560 895 ;
+C 154 ; WX 611 ; N Zcaron ; B 56 0 560 856 ;
+C 155 ; WX 611 ; N Zdotaccent ; B 56 0 560 891 ;
+C 156 ; WX 860.89 ; N IJ ; B 12 -22 798 683 ;
+C 157 ; WX 361 ; N Idotaccent ; B 12 0 321 877 ;
+C 158 ; WX 555.44 ; N dbar ; B 33 -11 528 694 ;
+C 159 ; WX 444.33 ; N section ; B 69 -205 374 705 ;
+C 160 ; WX 499.89 ; N abreve ; B 40 -11 493 694 ;
+C 161 ; WX 499.89 ; N aogonek ; B 40 -194 493 447 ;
+C 162 ; WX 444.33 ; N cacute ; B 33 -11 415 643 ;
+C 163 ; WX 444.33 ; N ccaron ; B 33 -11 415 604 ;
+C 164 ; WX 674.83 ; N dquoteright ; B 33 -11 665 694 ;
+C 165 ; WX 444.33 ; N ecaron ; B 28 -11 415 604 ;
+C 166 ; WX 444.33 ; N eogonek ; B 28 -194 415 447 ;
+C 167 ; WX 499.89 ; N gbreve ; B 28 -206 485 694 ;
+C 168 ; WX 277.72 ; N lacute ; B 25 0 282 907 ;
+C 169 ; WX 402.67 ; N lquoteright ; B 25 0 386 694 ;
+C 170 ; WX 277.72 ; N lslash ; B 18 0 258 694 ;
+C 171 ; WX 555.44 ; N nacute ; B 24 0 530 643 ;
+C 172 ; WX 555.44 ; N ncaron ; B 24 0 530 604 ;
+C 173 ; WX 555.44 ; N eng ; B 24 -205 452 441 ;
+C 174 ; WX 499.89 ; N ohungarumlaut ; B 28 -11 521 643 ;
+C 175 ; WX 391.56 ; N racute ; B 26 0 364 643 ;
+C 176 ; WX 391.56 ; N rcaron ; B 26 0 364 604 ;
+C 177 ; WX 394.33 ; N sacute ; B 33 -11 360 643 ;
+C 178 ; WX 394.33 ; N scaron ; B 33 -11 360 604 ;
+C 179 ; WX 394.33 ; N scedilla ; B 33 -205 360 447 ;
+C 180 ; WX 411 ; N tquoteright ; B 19 -11 401 704 ;
+C 181 ; WX 388.78 ; N tcedilla ; B 19 -205 359 615 ;
+C 182 ; WX 555.44 ; N uhungarumlaut ; B 24 -11 549 643 ;
+C 183 ; WX 555.44 ; N uring ; B 24 -11 530 661 ;
+C 184 ; WX 527.67 ; N ydieresis ; B 19 -205 508 586 ;
+C 185 ; WX 444.33 ; N zacute ; B 28 0 401 643 ;
+C 186 ; WX 444.33 ; N zcaron ; B 28 0 401 604 ;
+C 187 ; WX 444.33 ; N zdotaccent ; B 28 0 402 669 ;
+C 188 ; WX 583.17 ; N ij ; B 33 -205 485 669 ;
+C 189 ; WX 277.72 ; N exclamdown ; B 84 -216 194 500 ;
+C 190 ; WX 472.11 ; N questiondown ; B 56 -205 415 500 ;
+C 191 ; WX 638.72 ; N sterling ; B 56 -11 620 709 ;
+C 192 ; WX 749.83 ; N Agrave ; B 18 0 703 895 ;
+C 193 ; WX 749.83 ; N Aacute ; B 18 0 703 895 ;
+C 194 ; WX 749.83 ; N Acircumflex ; B 18 0 703 891 ;
+C 195 ; WX 749.83 ; N Atilde ; B 18 0 703 891 ;
+C 196 ; WX 749.83 ; N Adieresis ; B 18 0 703 839 ;
+C 197 ; WX 749.83 ; N Aring ; B 18 0 703 872 ;
+C 198 ; WX 902.56 ; N AE ; B 18 0 860 683 ;
+C 199 ; WX 722 ; N Ccedilla ; B 56 -205 665 705 ;
+C 200 ; WX 680.39 ; N Egrave ; B 17 0 637 895 ;
+C 201 ; WX 680.39 ; N Eacute ; B 17 0 637 895 ;
+C 202 ; WX 680.39 ; N Ecircumflex ; B 17 0 637 891 ;
+C 203 ; WX 680.39 ; N Edieresis ; B 17 0 637 839 ;
+C 204 ; WX 361 ; N Igrave ; B 12 0 321 895 ;
+C 205 ; WX 361 ; N Iacute ; B 12 0 321 895 ;
+C 206 ; WX 361 ; N Icircumflex ; B 12 0 321 891 ;
+C 207 ; WX 361 ; N Idieresis ; B 12 0 321 839 ;
+C 208 ; WX 763.72 ; N Eth ; B 19 0 693 683 ;
+C 209 ; WX 749.83 ; N Ntilde ; B 17 0 704 891 ;
+C 210 ; WX 777.61 ; N Ograve ; B 56 -22 721 895 ;
+C 211 ; WX 777.61 ; N Oacute ; B 56 -22 721 895 ;
+C 212 ; WX 777.61 ; N Ocircumflex ; B 56 -22 721 891 ;
+C 213 ; WX 777.61 ; N Otilde ; B 56 -22 721 891 ;
+C 214 ; WX 777.61 ; N Odieresis ; B 56 -22 721 839 ;
+C 215 ; WX 1013.67 ; N OE ; B 56 -22 971 705 ;
+C 216 ; WX 777.61 ; N Oslash ; B 56 -57 721 740 ;
+C 217 ; WX 749.83 ; N Ugrave ; B 17 -22 704 895 ;
+C 218 ; WX 749.83 ; N Uacute ; B 17 -22 704 895 ;
+C 219 ; WX 749.83 ; N Ucircumflex ; B 17 -22 704 891 ;
+C 220 ; WX 749.83 ; N Udieresis ; B 17 -22 704 839 ;
+C 221 ; WX 749.83 ; N Yacute ; B -3 0 724 895 ;
+C 222 ; WX 624.83 ; N Thorn ; B 20 0 554 683 ;
+C 223 ; WX 1110.83 ; N Germandbls ; B 56 -22 1054 705 ;
+C 224 ; WX 499.89 ; N agrave ; B 40 -11 493 643 ;
+C 225 ; WX 499.89 ; N aacute ; B 40 -11 493 643 ;
+C 226 ; WX 499.89 ; N acircumflex ; B 40 -11 493 639 ;
+C 227 ; WX 499.89 ; N atilde ; B 40 -11 493 669 ;
+C 228 ; WX 499.89 ; N adieresis ; B 40 -11 493 586 ;
+C 229 ; WX 499.89 ; N aring ; B 40 -11 493 661 ;
+C 230 ; WX 722 ; N ae ; B 43 -11 693 447 ;
+C 231 ; WX 444.33 ; N ccedilla ; B 33 -205 415 447 ;
+C 232 ; WX 444.33 ; N egrave ; B 28 -11 415 643 ;
+C 233 ; WX 444.33 ; N eacute ; B 28 -11 415 643 ;
+C 234 ; WX 444.33 ; N ecircumflex ; B 28 -11 415 639 ;
+C 235 ; WX 444.33 ; N edieresis ; B 28 -11 415 586 ;
+C 236 ; WX 277.72 ; N igrave ; B -6 0 243 643 ;
+C 237 ; WX 277.72 ; N iacute ; B 25 0 282 643 ;
+C 238 ; WX 277.72 ; N icircumflex ; B 3 0 274 639 ;
+C 239 ; WX 333.28 ; N idieresis ; B -9 0 289 586 ;
+C 240 ; WX 499.89 ; N eth ; B 28 -11 471 694 ;
+C 241 ; WX 555.44 ; N ntilde ; B 24 0 530 669 ;
+C 242 ; WX 499.89 ; N ograve ; B 28 -11 471 643 ;
+C 243 ; WX 499.89 ; N oacute ; B 28 -11 471 643 ;
+C 244 ; WX 499.89 ; N ocircumflex ; B 28 -11 471 639 ;
+C 245 ; WX 499.89 ; N otilde ; B 28 -11 471 669 ;
+C 246 ; WX 499.89 ; N odieresis ; B 28 -11 471 586 ;
+C 247 ; WX 777.61 ; N oe ; B 28 -11 749 447 ;
+C 248 ; WX 499.89 ; N oslash ; B 33 -103 466 534 ;
+C 249 ; WX 555.44 ; N ugrave ; B 24 -11 530 643 ;
+C 250 ; WX 555.44 ; N uacute ; B 24 -11 530 643 ;
+C 251 ; WX 555.44 ; N ucircumflex ; B 24 -11 530 639 ;
+C 252 ; WX 555.44 ; N udieresis ; B 24 -11 530 586 ;
+C 253 ; WX 527.67 ; N yacute ; B 19 -205 508 643 ;
+C 254 ; WX 555.44 ; N thorn ; B 25 -205 521 694 ;
+C 255 ; WX 499.89 ; N germandbls ; B 26 -11 471 705 ;
+EndCharMetrics
+StartKernData 
+StartKernPairs 471
+KPX A C -27.77
+KPX A G -27.77
+KPX A O -27.77
+KPX A Q -27.77
+KPX A T -83.31
+KPX A U -27.77
+KPX A V -111.08
+KPX A W -111.08
+KPX A Y -83.31
+KPX A t -27.77
+KPX Aacute C -27.77
+KPX Aacute G -27.77
+KPX Aacute O -27.77
+KPX Aacute Q -27.77
+KPX Aacute T -83.31
+KPX Aacute U -27.77
+KPX Aacute V -111.08
+KPX Aacute W -111.08
+KPX Aacute Y -83.31
+KPX Aacute t -27.77
+KPX Abreve C -27.77
+KPX Abreve G -27.77
+KPX Abreve O -27.77
+KPX Abreve Q -27.77
+KPX Abreve T -83.31
+KPX Abreve U -27.77
+KPX Abreve V -111.08
+KPX Abreve W -111.08
+KPX Abreve Y -83.31
+KPX Abreve t -27.77
+KPX Acircumflex C -27.77
+KPX Acircumflex G -27.77
+KPX Acircumflex O -27.77
+KPX Acircumflex Q -27.77
+KPX Acircumflex T -83.31
+KPX Acircumflex U -27.77
+KPX Acircumflex V -111.08
+KPX Acircumflex W -111.08
+KPX Acircumflex Y -83.31
+KPX Acircumflex t -27.77
+KPX Adieresis C -27.77
+KPX Adieresis G -27.77
+KPX Adieresis O -27.77
+KPX Adieresis Q -27.77
+KPX Adieresis T -83.31
+KPX Adieresis U -27.77
+KPX Adieresis V -111.08
+KPX Adieresis W -111.08
+KPX Adieresis Y -83.31
+KPX Adieresis t -27.77
+KPX Agrave C -27.77
+KPX Agrave G -27.77
+KPX Agrave O -27.77
+KPX Agrave Q -27.77
+KPX Agrave T -83.31
+KPX Agrave U -27.77
+KPX Agrave V -111.08
+KPX Agrave W -111.08
+KPX Agrave Y -83.31
+KPX Agrave t -27.77
+KPX Aogonek C -27.77
+KPX Aogonek G -27.77
+KPX Aogonek O -27.77
+KPX Aogonek Q -27.77
+KPX Aogonek T -83.31
+KPX Aogonek U -27.77
+KPX Aogonek V -111.08
+KPX Aogonek W -111.08
+KPX Aogonek Y -83.31
+KPX Aogonek t -27.77
+KPX Aring C -27.77
+KPX Aring G -27.77
+KPX Aring O -27.77
+KPX Aring Q -27.77
+KPX Aring T -83.31
+KPX Aring U -27.77
+KPX Aring V -111.08
+KPX Aring W -111.08
+KPX Aring Y -83.31
+KPX Aring t -27.77
+KPX Atilde C -27.77
+KPX Atilde G -27.77
+KPX Atilde O -27.77
+KPX Atilde Q -27.77
+KPX Atilde T -83.31
+KPX Atilde U -27.77
+KPX Atilde V -111.08
+KPX Atilde W -111.08
+KPX Atilde Y -83.31
+KPX Atilde t -27.77
+KPX D A -27.77
+KPX D V -27.77
+KPX D W -27.77
+KPX D X -27.77
+KPX D Y -27.77
+KPX F A -111.08
+KPX F C -27.77
+KPX F G -27.77
+KPX F O -27.77
+KPX F Q -27.77
+KPX F a -83.31
+KPX F e -83.31
+KPX F o -83.31
+KPX F r -83.31
+KPX F u -83.31
+KPX I I 27.77
+KPX K C -27.77
+KPX K G -27.77
+KPX K O -27.77
+KPX K Q -27.77
+KPX L T -83.31
+KPX L V -111.08
+KPX L W -111.08
+KPX L Y -83.31
+KPX O A -27.77
+KPX O V -27.77
+KPX O W -27.77
+KPX O X -27.77
+KPX O Y -27.77
+KPX P A -83.31
+KPX P a -27.77
+KPX P aacute -27.77
+KPX P acircumflex -27.77
+KPX P acircumflex -27.77
+KPX P adieresis -27.77
+KPX P ae -27.77
+KPX P agrave -27.77
+KPX P aogonek -27.77
+KPX P aring -27.77
+KPX P atilde -27.77
+KPX P cacute -27.77
+KPX P comma -83.31
+KPX P e -27.77
+KPX P eacute -27.77
+KPX P ecaron -27.77
+KPX P ecircumflex -27.77
+KPX P edieresis -27.77
+KPX P egrave -27.77
+KPX P eogonek -27.77
+KPX P o -27.77
+KPX P oacute -27.77
+KPX P ocircumflex -27.77
+KPX P odieresis -27.77
+KPX P oe -27.77
+KPX P ograve -27.77
+KPX P ohungarumlaut -27.77
+KPX P otilde -27.77
+KPX P period -83.31
+KPX R C -27.77
+KPX R G -27.77
+KPX R O -27.77
+KPX R Q -27.77
+KPX R T -83.31
+KPX R U -27.77
+KPX R V -111.08
+KPX R W -111.08
+KPX R Y -83.31
+KPX R t -27.77
+KPX T A -83.31
+KPX T a -83.31
+KPX T e -83.31
+KPX T o -83.31
+KPX T r -83.31
+KPX T u -83.31
+KPX T y -27.77
+KPX V A -111.08
+KPX V C -27.77
+KPX V G -27.77
+KPX V O -27.77
+KPX V Q -27.77
+KPX V a -83.31
+KPX V e -83.31
+KPX V o -83.31
+KPX V r -83.31
+KPX V u -83.31
+KPX W A -111.08
+KPX W C -27.77
+KPX W G -27.77
+KPX W O -27.77
+KPX W Q -27.77
+KPX W a -83.31
+KPX W e -83.31
+KPX W o -83.31
+KPX W r -83.31
+KPX W u -83.31
+KPX X C -27.77
+KPX X G -27.77
+KPX X O -27.77
+KPX X Q -27.77
+KPX Y A -83.31
+KPX Y a -83.31
+KPX Y e -83.31
+KPX Y o -83.31
+KPX Y r -83.31
+KPX Y u -83.31
+KPX a j 55.54
+KPX a v -27.77
+KPX a w -27.77
+KPX a y -27.77
+KPX b c 27.77
+KPX b cacute 27.77
+KPX b ccaron 27.77
+KPX b ccedilla 27.77
+KPX b d 27.77
+KPX b e 27.77
+KPX b eacute 27.77
+KPX b ecaron 27.77
+KPX b ecircumflex 27.77
+KPX b edieresis 27.77
+KPX b egrave 27.77
+KPX b eogonek 27.77
+KPX b j 55.54
+KPX b o 27.77
+KPX b oacute 27.77
+KPX b ocircumflex 27.77
+KPX b odieresis 27.77
+KPX b oe 27.77
+KPX b ograve 27.77
+KPX b ohungarumlaut 27.77
+KPX b otilde 27.77
+KPX b q 27.77
+KPX b v -27.77
+KPX b w -27.77
+KPX b x -27.77
+KPX b y -27.77
+KPX c h -27.77
+KPX c k -27.77
+KPX f bracketright 77.76
+KPX f exclam 77.76
+KPX f parenright 77.76
+KPX f question 77.76
+KPX f quoteright 77.76
+KPX ff bracketright 77.76
+KPX ff exclam 77.76
+KPX ff parenright 77.76
+KPX ff question 77.76
+KPX ff quoteright 77.76
+KPX g j 27.77
+KPX h b -27.77
+KPX h t -27.77
+KPX h u -27.77
+KPX h uacute -27.77
+KPX h ucircumflex -27.77
+KPX h udieresis -27.77
+KPX h ugrave -27.77
+KPX h uhungarumlaut -27.77
+KPX h uring -27.77
+KPX h v -27.77
+KPX h w -27.77
+KPX h y -27.77
+KPX k a -27.77
+KPX k a -55.54
+KPX k aacute -27.77
+KPX k aacute -55.54
+KPX k acircumflex -55.54
+KPX k acircumflex -55.54
+KPX k acircumflex -27.77
+KPX k acircumflex -27.77
+KPX k adieresis -27.77
+KPX k adieresis -55.54
+KPX k ae -55.54
+KPX k ae -27.77
+KPX k agrave -55.54
+KPX k agrave -27.77
+KPX k aogonek -55.54
+KPX k aogonek -27.77
+KPX k aring -55.54
+KPX k aring -27.77
+KPX k atilde -27.77
+KPX k atilde -55.54
+KPX k c -27.77
+KPX k cacute -27.77
+KPX k cacute -27.77
+KPX k cacute -55.54
+KPX k ccaron -27.77
+KPX k ccedilla -27.77
+KPX k e -27.77
+KPX k eacute -27.77
+KPX k ecaron -27.77
+KPX k ecircumflex -27.77
+KPX k edieresis -27.77
+KPX k egrave -27.77
+KPX k eogonek -27.77
+KPX k o -27.77
+KPX k oacute -27.77
+KPX k ocircumflex -27.77
+KPX k odieresis -27.77
+KPX k oe -27.77
+KPX k ograve -27.77
+KPX k ohungarumlaut -27.77
+KPX k otilde -27.77
+KPX m b -27.77
+KPX m t -27.77
+KPX m u -27.77
+KPX m uacute -27.77
+KPX m ucircumflex -27.77
+KPX m udieresis -27.77
+KPX m ugrave -27.77
+KPX m uhungarumlaut -27.77
+KPX m uring -27.77
+KPX m v -27.77
+KPX m w -27.77
+KPX m y -27.77
+KPX n b -27.77
+KPX n t -27.77
+KPX n u -27.77
+KPX n uacute -27.77
+KPX n ucircumflex -27.77
+KPX n udieresis -27.77
+KPX n ugrave -27.77
+KPX n uhungarumlaut -27.77
+KPX n uring -27.77
+KPX n v -27.77
+KPX n w -27.77
+KPX n y -27.77
+KPX o c 27.77
+KPX o cacute 27.77
+KPX o ccaron 27.77
+KPX o ccedilla 27.77
+KPX o d 27.77
+KPX o e 27.77
+KPX o eacute 27.77
+KPX o ecaron 27.77
+KPX o ecircumflex 27.77
+KPX o edieresis 27.77
+KPX o egrave 27.77
+KPX o eogonek 27.77
+KPX o j 55.54
+KPX o o 27.77
+KPX o oacute 27.77
+KPX o ocircumflex 27.77
+KPX o odieresis 27.77
+KPX o oe 27.77
+KPX o ograve 27.77
+KPX o ohungarumlaut 27.77
+KPX o otilde 27.77
+KPX o q 27.77
+KPX o v -27.77
+KPX o w -27.77
+KPX o x -27.77
+KPX o y -27.77
+KPX p c 27.77
+KPX p cacute 27.77
+KPX p ccaron 27.77
+KPX p ccedilla 27.77
+KPX p d 27.77
+KPX p e 27.77
+KPX p eacute 27.77
+KPX p ecaron 27.77
+KPX p ecircumflex 27.77
+KPX p edieresis 27.77
+KPX p egrave 27.77
+KPX p eogonek 27.77
+KPX p j 55.54
+KPX p o 27.77
+KPX p oacute 27.77
+KPX p ocircumflex 27.77
+KPX p odieresis 27.77
+KPX p oe 27.77
+KPX p ograve 27.77
+KPX p ohungarumlaut 27.77
+KPX p otilde 27.77
+KPX p q 27.77
+KPX p v -27.77
+KPX p w -27.77
+KPX p x -27.77
+KPX p y -27.77
+KPX quoteright exclam 111.08
+KPX quoteright question 111.08
+KPX t w -27.77
+KPX t y -27.77
+KPX u w -27.77
+KPX v a -27.77
+KPX v a -55.54
+KPX v aacute -27.77
+KPX v aacute -55.54
+KPX v acircumflex -55.54
+KPX v acircumflex -27.77
+KPX v acircumflex -27.77
+KPX v acircumflex -55.54
+KPX v adieresis -27.77
+KPX v adieresis -55.54
+KPX v ae -27.77
+KPX v ae -55.54
+KPX v agrave -27.77
+KPX v agrave -55.54
+KPX v aogonek -55.54
+KPX v aogonek -27.77
+KPX v aring -55.54
+KPX v aring -27.77
+KPX v atilde -55.54
+KPX v atilde -27.77
+KPX v c -27.77
+KPX v cacute -55.54
+KPX v cacute -27.77
+KPX v cacute -27.77
+KPX v ccaron -27.77
+KPX v ccedilla -27.77
+KPX v e -27.77
+KPX v eacute -27.77
+KPX v ecaron -27.77
+KPX v ecircumflex -27.77
+KPX v edieresis -27.77
+KPX v egrave -27.77
+KPX v eogonek -27.77
+KPX v o -27.77
+KPX v oacute -27.77
+KPX v ocircumflex -27.77
+KPX v odieresis -27.77
+KPX v oe -27.77
+KPX v ograve -27.77
+KPX v ohungarumlaut -27.77
+KPX v otilde -27.77
+KPX w a -27.77
+KPX w aacute -27.77
+KPX w acircumflex -27.77
+KPX w acircumflex -27.77
+KPX w adieresis -27.77
+KPX w ae -27.77
+KPX w agrave -27.77
+KPX w aogonek -27.77
+KPX w aring -27.77
+KPX w atilde -27.77
+KPX w c -27.77
+KPX w cacute -27.77
+KPX w cacute -27.77
+KPX w ccaron -27.77
+KPX w ccedilla -27.77
+KPX w e -27.77
+KPX w eacute -27.77
+KPX w ecaron -27.77
+KPX w ecircumflex -27.77
+KPX w edieresis -27.77
+KPX w egrave -27.77
+KPX w eogonek -27.77
+KPX w o -27.77
+KPX w oacute -27.77
+KPX w ocircumflex -27.77
+KPX w odieresis -27.77
+KPX w oe -27.77
+KPX w ograve -27.77
+KPX w ohungarumlaut -27.77
+KPX w otilde -27.77
+KPX y a -27.77
+KPX y aacute -27.77
+KPX y acircumflex -27.77
+KPX y acircumflex -27.77
+KPX y adieresis -27.77
+KPX y ae -27.77
+KPX y agrave -27.77
+KPX y aogonek -27.77
+KPX y aring -27.77
+KPX y atilde -27.77
+KPX y cacute -27.77
+KPX y comma -83.31
+KPX y e -27.77
+KPX y eacute -27.77
+KPX y ecaron -27.77
+KPX y ecircumflex -27.77
+KPX y edieresis -27.77
+KPX y egrave -27.77
+KPX y eogonek -27.77
+KPX y o -27.77
+KPX y oacute -27.77
+KPX y ocircumflex -27.77
+KPX y odieresis -27.77
+KPX y oe -27.77
+KPX y ograve -27.77
+KPX y ohungarumlaut -27.77
+KPX y otilde -27.77
+KPX y period -83.31
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/fontfiles/dcr10.pfb b/fontfiles/dcr10.pfb
new file mode 100644 (file)
index 0000000..34cb0ea
Binary files /dev/null and b/fontfiles/dcr10.pfb differ
diff --git a/fontfiles/dodge.ttf b/fontfiles/dodge.ttf
new file mode 100644 (file)
index 0000000..132b95b
Binary files /dev/null and b/fontfiles/dodge.ttf differ
diff --git a/gaussian.c b/gaussian.c
new file mode 100644 (file)
index 0000000..13cfd31
--- /dev/null
@@ -0,0 +1,71 @@
+#include "image.h"
+
+float
+gauss(int x,float std) {
+  return 1.0/(sqrt(2.0*PI)*std)*exp(-(float)(x)*(float)(x)/(2*std*std));
+}
+
+/* Counters are as follows
+ l:  lines
+ i:  columns
+ c:  filter coeffs
+ ch: channels
+ pc: coeff equalization
+*/
+
+
+
+void
+i_gaussian(i_img *im,float stdev) {
+  int i,l,c,ch;
+  float pc;
+  float coeff[21];
+  i_color rcolor;
+  float res[11];
+  i_img timg;
+
+  mm_log((1,"i_gaussian(im* 0x%x,stdev %.2f)\n",im,stdev));
+  
+  i_img_empty_ch(&timg,im->xsize,im->ysize,im->channels);
+             
+  for(i=0;i<11;i++) coeff[10+i]=coeff[10-i]=gauss(i,stdev);
+  pc=0;
+  for(i=0;i<21;i++) pc+=coeff[i];
+  for(i=0;i<21;i++) coeff[i]/=pc;
+
+
+  for(l=0;l<im->ysize;l++) {
+    for(i=0;i<im->xsize;i++) {
+      pc=0.0;
+      for(ch=0;ch<im->channels;ch++) res[ch]=0; 
+      for(c=0;c<21;c++)
+       if (i_gpix(im,i+c-10,l,&rcolor)!=-1) {
+         for(ch=0;ch<im->channels;ch++) res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
+         pc+=coeff[c];
+       }
+      for(ch=0;ch<im->channels;ch++) rcolor.channel[ch]=(unsigned char)(((res[ch]/(float)(pc)>255.0)?255.0:res[ch]/(float)(pc)));
+      i_ppix(&timg,i,l,&rcolor);
+    }
+  }
+  
+  for(l=0;l<im->xsize;l++) {
+    for(i=0;i<im->ysize;i++) {
+      pc=0.0;
+      for(ch=0;ch<im->channels;ch++) res[ch]=0; 
+      for(c=0;c<21;c++)
+       if (i_gpix(&timg,l,i+c-10,&rcolor)!=-1) {
+         for(ch=0;ch<im->channels;ch++) res[ch]+=(float)(rcolor.channel[ch])*coeff[c];
+         pc+=coeff[c];
+       }
+      for(ch=0;ch<im->channels;ch++) rcolor.channel[ch]=(unsigned char)(((res[ch]/(float)(pc)>255.0)?255.0:res[ch]/(float)(pc)));
+      i_ppix(im,l,i,&rcolor);
+    }
+  }
+}
+
+
+
+
+
+
+
diff --git a/gif.c b/gif.c
new file mode 100644 (file)
index 0000000..55f923a
--- /dev/null
+++ b/gif.c
@@ -0,0 +1,839 @@
+#include "image.h"
+#include <gif_lib.h>
+
+
+#if IM_GIFMAJOR >= 4
+
+struct gif_scalar_info {
+  char *data;
+  int length;
+  int cpos;
+};
+
+int
+my_gif_inputfunc(GifFileType* gft, GifByteType *buf,int length) {
+  struct gif_scalar_info *gsi=(struct gif_scalar_info *)gft->UserData;
+  /*   fprintf(stderr,"my_gif_inputfunc: length=%d cpos=%d tlength=%d\n",length,gsi->cpos,gsi->length); */
+
+  if (gsi->cpos == gsi->length) return 0;
+  if (gsi->cpos+length > gsi->length) length=gsi->length-gsi->cpos; /* Don't read too much */
+  memcpy(buf,gsi->data+gsi->cpos,length);
+  gsi->cpos+=length;
+  return length;
+}
+
+#endif
+
+/*
+  This file needs a complete rewrite 
+
+  This file needs a complete rewrite 
+
+  Maybe not anymore, though reading still needs to support reading
+  all those gif properties.
+*/
+
+/* Make some variables global, so we could access them faster: */
+
+static int
+    ImageNum = 0,
+    BackGround = 0,
+    ColorMapSize = 0,
+    InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
+    InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
+static ColorMapObject *ColorMap;
+
+i_img *
+i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
+  i_img *im;
+  int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
+  GifRecordType RecordType;
+  GifByteType *Extension;
+  
+  GifRowType GifRow;
+  static&n