--- /dev/null
+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/
+
+===================================================
+
--- /dev/null
+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
--- /dev/null
+#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);
+
+
+
+
--- /dev/null
+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
--- /dev/null
+
+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";
+ }
+
+}
--- /dev/null
+================================================================
+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.
--- /dev/null
+#!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
--- /dev/null
+#!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;
--- /dev/null
+#!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
+
--- /dev/null
+#!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
--- /dev/null
+#!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
+
--- /dev/null
+#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);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
--- /dev/null
+#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;
+}
--- /dev/null
+#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
+
--- /dev/null
+#!/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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
--- /dev/null
+#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
+
--- /dev/null
+#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_ */
+
--- /dev/null
+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)
+
+';
+
+}
+}
--- /dev/null
+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
--- /dev/null
+#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\"> </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 */
--- /dev/null
+#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 */
+
--- /dev/null
+#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 */
+
--- /dev/null
+#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 */
+
--- /dev/null
+#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;
+}
--- /dev/null
+
+
+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);
+ }
+}
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+#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;
--- /dev/null
+#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);
+}
--- /dev/null
+#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};
+
--- /dev/null
+#!/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 );
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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 */
--- /dev/null
+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
--- /dev/null
+#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);
+ }
+ }
+}
+
+
+
+
+
+
+
--- /dev/null
+#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