improved support for threads
authorTony Cook <tony@develop-help.com>
Fri, 14 Dec 2012 09:44:16 +0000 (20:44 +1100)
committerTony Cook <tony@develop-help.com>
Fri, 14 Dec 2012 09:44:16 +0000 (20:44 +1100)
110 files changed:
Changes
FT2/Changes
FT2/FT2.pm
FT2/FT2.xs
FT2/MANIFEST
FT2/Makefile.PL
FT2/freetyp2.c
FT2/imft2.h
FT2/t/t20thread.t [new file with mode: 0644]
GIF/GIF.pm
GIF/GIF.xs
GIF/imgif.c
GIF/imgif.h
Imager.pm
Imager.xs
MANIFEST
MANIFEST.SKIP
Makefile.PL
T1/T1.pm
T1/T1.xs
T1/imt1.c
T1/imt1.h
T1/t/t10type1.t
T1/t/t30thread.t [new file with mode: 0644]
T1/typemap [new file with mode: 0644]
TIFF/TIFF.pm
TIFF/TIFF.xs
TIFF/imtiff.c
TIFF/imtiff.h
apidocs.perl
bench/.gitignore [new file with mode: 0644]
bench/gifthread.pl [new file with mode: 0644]
bench/largish.jpg [new file with mode: 0644]
bench/tifthread.pl [new file with mode: 0644]
bmp.c
context.c [new file with mode: 0644]
conv.im
convert.im
datatypes.c
draw.c
dynaload.c
error.c
ext.h
fills.c
filters.im
flip.im
font.c [deleted file]
fontft1.c [new file with mode: 0644]
gaussian.im
hlines.c
image.c
imager.h
imageri.h
imcover.perl
imdatatypes.h
imerror.h
imext.c
imext.h
imexttypes.h
img16.c
img8.c
imgdouble.c
immacros.h
iolayer.c
iolayer.h
iolayert.h
lib/Imager/API.pod
lib/Imager/APIRef.pod
lib/Imager/Font/Test.pm [new file with mode: 0644]
lib/Imager/Test.pm
lib/Imager/Threads.pod [new file with mode: 0644]
limits.c
log.c
log.h
maskimg.c
mutexnull.c [new file with mode: 0644]
mutexpthr.c [new file with mode: 0644]
mutexwin.c [new file with mode: 0644]
palimg.c
plug.h
t/t01introvert.t
t/t020masked.t
t/t021sixteen.t
t/t022double.t
t/t023palette.t
t/t080log.t [new file with mode: 0644]
t/t081error.t [new file with mode: 0644]
t/t082limit.t [new file with mode: 0644]
t/t1000files.t
t/t21draw.t
t/t31font.t
t/t40scale.t
t/t55trans.t
t/t58trans2.t
t/t61filters.t
t/t62compose.t
t/t63combine.t
t/t64copyflip.t
t/t65crop.t
t/t66paste.t
t/t67convert.t
t/t68map.t
t/t69rubthru.t
t/t82inline.t
t/t84inlinectx.t [new file with mode: 0644]
t/t90cc.t
t/t99thread.t
t/x20spell.t
t/x90cmpversion.t
typemap.local

diff --git a/Changes b/Changes
index 070a568..2e8aa34 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,18 @@
 Imager release history.  Older releases can be found in Changes.old
 
+ - improved thread safety
+   - the internal error stack and log file handle are now in a per-thread
+     context object
+   - JPEG now captures IPTC information in a thread-safe way
+   - avoid globals where possible for warning capture in libtiff
+   - use a mutex to avoid re-entering thread-unsafe giflib
+   - use a mutex to avoid re-entering thread-unsafe tifflib
+   - use a mutex to avoid re-entering thread-unsafe T1Lib
+   - use a library handle per thread for freetype 2.
+   - use an engine handle per thread for freetype 1.x.
+   - originally these changes broke ABI compatibility, this has been
+     restored.
+
  - clarify the return value of getpixel();
    https://rt.cpan.org/Ticket/Display.html?id=81198
 
index f02892a..fea57ce 100644 (file)
@@ -1,5 +1,7 @@
  - report the library version during testing
 
+ - improve thread safety
+
 Imager-Font-FT2 0.86
 ====================
 
index 1178700..c67434c 100644 (file)
@@ -1,6 +1,7 @@
 package Imager::Font::FT2;
 use strict;
 use Imager;
+use Scalar::Util ();
 use vars qw($VERSION @ISA);
 @ISA = qw(Imager::Font);
 
@@ -50,6 +51,10 @@ sub new {
 
 sub _draw {
   my $self = shift;
+
+  $self->_valid
+    or return;
+
   my %input = @_;
   if (exists $input{channel}) {
     i_ft2_cp($self->{id}, $input{image}{IMG}, $input{'x'}, $input{'y'},
@@ -69,12 +74,19 @@ sub _bounding_box {
   my $self = shift;
   my %input = @_;
 
+  $self->_valid
+    or return;
+
   return i_ft2_bbox($self->{id}, $input{size}, $input{sizew}, $input{string}, 
                    $input{utf8});
 }
 
 sub dpi {
   my $self = shift;
+
+  $self->_valid
+    or return;
+
   my @old = i_ft2_getdpi($self->{id});
   if (@_) {
     my %hsh = @_;
@@ -97,12 +109,18 @@ sub dpi {
 sub hinting {
   my ($self, %opts) = @_;
 
+  $self->_valid
+    or return;
+
   i_ft2_sethinting($self->{id}, $opts{hinting} || 0);
 }
 
 sub _transform {
   my $self = shift;
 
+  $self->_valid
+    or return;
+
   my %hsh = @_;
   my $matrix = $hsh{matrix} or return undef;
 
@@ -117,6 +135,9 @@ sub utf8 {
 sub has_chars {
   my ($self, %hsh) = @_;
 
+  $self->_valid
+    or return;
+
   unless (defined $hsh{string} && length $hsh{string}) {
     $Imager::ERRSTR = "No string supplied to \$font->has_chars()";
     return;
@@ -128,6 +149,9 @@ sub has_chars {
 sub face_name {
   my ($self) = @_;
 
+  $self->_valid
+    or return;
+
   i_ft2_face_name($self->{id});
 }
 
@@ -138,6 +162,9 @@ sub can_glyph_names {
 sub glyph_names {
   my ($self, %input) = @_;
 
+  $self->_valid
+    or return;
+
   my $string = $input{string};
   defined $string
     or return Imager->_set_error("no string parameter passed to glyph_names");
@@ -154,12 +181,18 @@ sub glyph_names {
 sub is_mm {
   my ($self) = @_;
 
+  $self->_valid
+    or return;
+
   i_ft2_is_multiple_master($self->{id});
 }
 
 sub mm_axes {
   my ($self) = @_;
 
+  $self->_valid
+    or return;
+
   my ($num_axis, $num_design, @axes) =
     i_ft2_get_multiple_masters($self->{id})
       or return Imager->_set_error(Imager->_error_as_msg);
@@ -170,6 +203,9 @@ sub mm_axes {
 sub set_mm_coords {
   my ($self, %opts) = @_;
 
+  $self->_valid
+    or return;
+
   $opts{coords}
     or return Imager->_set_error("Missing coords parameter");
   ref($opts{coords}) && $opts{coords} =~ /ARRAY\(0x[\da-f]+\)$/
@@ -180,6 +216,19 @@ sub set_mm_coords {
 
   return 1;
 }
+
+# objects may be invalidated on thread creation (or Win32 fork emulation)
+sub _valid {
+  my $self = shift;
+
+  unless ($self->{id} && Scalar::Util::blessed($self->{id})) {
+    Imager->_set_error("font object was created in another thread");
+    return;
+  }
+
+  return 1;
+}
+
 1;
 
 __END__
index 95bf495..631b147 100644 (file)
@@ -369,3 +369,4 @@ i_ft2_set_mm_coords(handle, ...)
 
 BOOT:
        PERL_INITIALIZE_IMAGER_CALLBACKS;
+       i_ft2_start();
index 0f154ff..4e4becf 100644 (file)
@@ -16,4 +16,5 @@ MANIFEST                      This list of files
 MANIFEST.SKIP
 README
 t/t10ft2.t
+t/t20thread.t
 typemap
index 81d8176..79b45ef 100644 (file)
@@ -61,6 +61,7 @@ else {
     $opts{PREREQ_PM} =
       {
        @Imager_req,
+       'Scalar::Util' => 1.00,
        XSLoader => 0,
       };
   }
index e7bd850..6431ad2 100644 (file)
@@ -50,8 +50,18 @@ Truetype, Type1 and Windows FNT.
 
 static void ft2_push_message(int code);
 
-static int ft2_initialized = 0;
-static FT_Library library;
+static void ft2_final(void *);
+
+static im_slot_t slot = -1;
+
+typedef struct {
+  int initialized;
+  FT_Library library;
+  im_context_t ctx;
+} ft2_state;
+
+static ft2_state *
+i_ft2_init(void);
 
 static i_img_dim i_min(i_img_dim a, i_img_dim b);
 static i_img_dim i_max(i_img_dim a, i_img_dim b);
@@ -59,22 +69,25 @@ static i_img_dim i_max(i_img_dim a, i_img_dim b);
 int
 i_ft2_version(int runtime, char *buf, size_t buf_size) {
   char work[100];
-  i_clear_error();
 
-  if (!ft2_initialized && !i_ft2_init())
-    return NULL;
+  i_clear_error();
 
   if (buf_size == 0) {
     i_push_error(0, "zero size buffer supplied");
     return 0;
   }
   if (runtime) {
+    ft2_state *ft2;
     /* initialized to work around a bug in FT2
        http://lists.nongnu.org/archive/html/freetype-devel/2002-09/msg00058.html
        Though I don't know why I still see this in 2.4.2
      */
     FT_Int major = 1, minor = 1, patch = 1;
-    FT_Library_Version(library, &major, &minor, &patch);
+
+    if ((ft2 = i_ft2_init()) == NULL)
+      return 0;
+
+    FT_Library_Version(ft2->library, &major, &minor, &patch);
     sprintf(work, "%d.%d.%d", (int)major, (int)minor, (int)patch);
   }
   else {
@@ -86,34 +99,71 @@ i_ft2_version(int runtime, char *buf, size_t buf_size) {
   return 1;
 }
 
+void
+i_ft2_start(void) {
+  if (slot == -1)
+    slot = im_context_slot_new(ft2_final);
+}
+
 /*
 =item i_ft2_init(void)
 
 Initializes the Freetype 2 library.
 
-Returns true on success, false on failure.
+Returns ft2_state * on success or NULL on failure.
 
 =cut
 */
-int
+
+static ft2_state *
 i_ft2_init(void) {
   FT_Error error;
+  im_context_t ctx = im_get_context();
+  ft2_state *ft2 = im_context_slot_get(ctx, slot);
+
+  if (ft2 == NULL) {
+    ft2 = mymalloc(sizeof(ft2_state));
+    ft2->initialized = 0;
+    ft2->library = NULL;
+    ft2->ctx = ctx;
+    im_context_slot_set(ctx, slot, ft2);
+    mm_log((1, "created FT2 state %p for context %p\n", ft2, ctx));
+  }
 
   i_clear_error();
-  error = FT_Init_FreeType(&library);
-  if (error) {
-    ft2_push_message(error);
-    i_push_error(0, "Initializing Freetype2");
-    return 0;
+  if (!ft2->initialized) {
+    error = FT_Init_FreeType(&ft2->library);
+    if (error) {
+      ft2_push_message(error);
+      i_push_error(0, "Initializing Freetype2");
+      return NULL;
+    }
+    mm_log((1, "initialized FT2 state %p\n", ft2));
+
+    ft2->initialized = 1;
   }
 
-  ft2_initialized = 1;
+  return ft2;
+}
 
-  return 1;
+static void
+ft2_final(void *state) {
+  ft2_state *ft2 = state;
+
+  if (ft2->initialized) {
+    mm_log((1, "finalizing FT2 state %p\n", state));
+    FT_Done_FreeType(ft2->library);
+    ft2->library = NULL;
+    ft2->initialized = 0;
+  }
+
+  mm_log((1, "freeing FT2 state %p\n", state));
+  myfree(state);
 }
 
 struct FT2_Fonthandle {
   FT_Face face;
+  ft2_state *state;
   int xdpi, ydpi;
   int hint;
   FT_Encoding encoding;
@@ -169,14 +219,15 @@ i_ft2_new(const char *name, int index) {
   int i, j;
   FT_Encoding encoding;
   int score;
+  ft2_state *ft2;
 
   mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
 
-  if (!ft2_initialized && !i_ft2_init())
+  if ((ft2 = i_ft2_init()) == NULL)
     return NULL;
 
   i_clear_error();
-  error = FT_New_Face(library, name, index, &face);
+  error = FT_New_Face(ft2->library, name, index, &face);
   if (error) {
     ft2_push_message(error);
     i_push_error(error, "Opening face");
@@ -204,6 +255,7 @@ i_ft2_new(const char *name, int index) {
 
   result = mymalloc(sizeof(FT2_Fonthandle));
   result->face = face;
+  result->state = ft2;
   result->xdpi = result->ydpi = 72;
   result->encoding = encoding;
 
@@ -275,7 +327,7 @@ i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
   if (xdpi > 0 && ydpi > 0) {
     handle->xdpi = xdpi;
     handle->ydpi = ydpi;
-    return 0;
+    return 1;
   }
   else {
     i_push_error(0, "resolutions must be positive");
index 842af88..2153a33 100644 (file)
@@ -7,8 +7,8 @@ typedef struct FT2_Fonthandle FT2_Fonthandle;
 
 typedef FT2_Fonthandle* Imager__Font__FT2x;
 
-extern int i_ft2_init(void);
 extern int i_ft2_version(int runtime, char *buf, size_t buf_size);
+extern void i_ft2_start(void);
 extern FT2_Fonthandle * i_ft2_new(const char *name, int index);
 extern void i_ft2_destroy(FT2_Fonthandle *handle);
 extern int i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi);
diff --git a/FT2/t/t20thread.t b/FT2/t/t20thread.t
new file mode 100644 (file)
index 0000000..723189d
--- /dev/null
@@ -0,0 +1,59 @@
+#!perl -w
+use strict;
+use Imager;
+
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's lack of threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's lack of threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+# https://rt.cpan.org/Ticket/Display.html?id=65812
+# https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100
+$Test::More::VERSION =~ /^2\.00_/
+  and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*";
+
+plan tests => 8;
+
+Imager->open_log(log => "testout/t20thread.log");
+
+my $ft1 = Imager::Font->new(file => "fontfiles/dodge.ttf", type => "ft2");
+ok($ft1, "make a font");
+ok($ft1->_valid, "and it's valid");
+my $ft2;
+
+my $thr = threads->create
+  (
+   sub {
+     ok(!$ft1->_valid, "first font no longer valid");
+     $ft2 = Imager::Font->new(file => "fontfiles/dodge.ttf", type => "ft2");
+     ok($ft2, "make a new font in thread");
+     ok($ft2->_valid, "and it's valid");
+     1;
+   },
+  );
+
+ok($thr->join, "join the thread");
+ok($ft1->_valid, "original font still valid in main thread");
+is($ft2, undef, "font created in thread shouldn't be set in main thread");
+
+Imager->close_log();
index 5d92f7a..05fb1f9 100644 (file)
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.85";
+  $VERSION = "0.86";
 
   require XSLoader;
   XSLoader::load('Imager::File::GIF', $VERSION);
index 75687b8..7641b8c 100644 (file)
@@ -147,3 +147,4 @@ i_readgif_multi_wiol(ig)
 BOOT:
        PERL_INITIALIZE_IMAGER_CALLBACKS;
        PERL_INITIALIZE_IMAGER_PERL_CALLBACKS;
+       i_init_gif();
index 8dfe834..9b6e6b1 100644 (file)
@@ -66,7 +66,8 @@ functionality with giflib3.
 #define myGifError(gif) ((gif)->Error)
 #define MakeMapObject GifMakeMapObject
 #define FreeMapObject GifFreeMapObject
-
+#define gif_mutex_lock(mutex)
+#define gif_mutex_unlock(mutex)
 #else
 #define PRE_SET_VERSION
 static GifFileType *
@@ -86,6 +87,8 @@ myEGifOpen(void *userPtr, OutputFunc outputFunc, int *error) {
   return result;
 }
 #define myGifError(gif) GifLastError()
+#define gif_mutex_lock(mutex) i_mutex_lock(mutex)
+#define gif_mutex_unlock(mutex) i_mutex_unlock(mutex)
 
 #endif
 
@@ -98,6 +101,17 @@ static const int
   InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
   InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
 
+#if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5
+static i_mutex_t mutex;
+#endif
+
+void
+i_init_gif(void) {
+#if !defined(GIFLIB_MAJOR) || GIFLIB_MAJOR < 5
+  mutex = i_mutex_new();
+#endif
+}
+
 static
 void
 i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
@@ -877,6 +891,9 @@ i_img **
 i_readgif_multi_wiol(io_glue *ig, int *count) {
   GifFileType *GifFile;
   int gif_error;
+  i_img **result;
+
+  gif_mutex_lock(mutex);
 
   i_clear_error();
   
@@ -884,10 +901,15 @@ i_readgif_multi_wiol(io_glue *ig, int *count) {
     gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
-  return i_readgif_multi_low(GifFile, count, -1);
+  result = i_readgif_multi_low(GifFile, count, -1);
+
+  gif_mutex_unlock(mutex);
+
+  return result;
 }
 
 static int
@@ -901,6 +923,9 @@ i_img *
 i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
   GifFileType *GifFile;
   int gif_error;
+  i_img *result;
+
+  gif_mutex_lock(mutex);
 
   i_clear_error();
 
@@ -908,10 +933,15 @@ i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
     gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
-  return i_readgif_low(GifFile, color_table, colors);
+  result = i_readgif_low(GifFile, color_table, colors);
+
+  gif_mutex_unlock(mutex);
+
+  return result;
 }
 
 /*
@@ -957,6 +987,7 @@ i_img *
 i_readgif_single_wiol(io_glue *ig, int page) {
   GifFileType *GifFile;
   int gif_error;
+  i_img *result;
 
   i_clear_error();
   if (page < 0) {
@@ -964,15 +995,21 @@ i_readgif_single_wiol(io_glue *ig, int page) {
     return NULL;
   }
 
+  gif_mutex_lock(mutex);
 
   if ((GifFile = myDGifOpen((void *)ig, io_glue_read_cb, &gif_error )) == NULL) {
     gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+    gif_mutex_unlock(mutex);
     return NULL;
   }
     
-  return i_readgif_single_low(GifFile, page);
+  result = i_readgif_single_low(GifFile, page);
+
+  gif_mutex_unlock(mutex);
+
+  return result;
 }
 
 /*
@@ -1830,6 +1867,8 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
   int gif_error;
   int result;
 
+  gif_mutex_lock(mutex);
+
   i_clear_error();
 
 #ifdef PRE_SET_VERSION
@@ -1840,6 +1879,7 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
     gif_push_error(gif_error);
     i_push_error(0, "Cannot create giflib callback object");
     mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
+    gif_mutex_unlock(mutex);
     return 0;
   }
   
@@ -1849,6 +1889,8 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
 
   result = i_writegif_low(quant, GifFile, imgs, count);
   
+  gif_mutex_unlock(mutex);
+
   if (i_io_close(ig))
     return 0;
   
@@ -1970,6 +2012,8 @@ gif_push_error(int code) {
 
 Arnar M. Hrafnkelsson, addi@umich.edu
 
+Tony Cook <tonyc@cpan.org>
+
 =head1 SEE ALSO
 
 perl(1), Imager(3)
index ca92af5..9a92e8c 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "imext.h"
 
+void i_init_gif(void);
 double i_giflib_version(void);
 i_img *i_readgif_wiol(io_glue *ig, int **colour_table, int *colours);
 i_img *i_readgif_single_wiol(io_glue *ig, int page);
index d841714..b15b405 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -3,7 +3,7 @@ package Imager;
 use strict;
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR %OPCODES $I2P $FORMATGUESS $warn_obsolete);
 use IO::File;
-
+use Scalar::Util;
 use Imager::Color;
 use Imager::Font;
 
@@ -619,9 +619,9 @@ sub _combine {
 sub _valid_image {
   my ($self, $method) = @_;
 
-  $self->{IMG} and return 1;
+  $self->{IMG} && Scalar::Util::blessed($self->{IMG}) and return 1;
 
-  my $msg = 'empty input image';
+  my $msg = $self->{IMG} ? "images do not cross threads" : "empty input image";
   $msg = "$method: $msg" if $method;
   $self->_set_error($msg);
 
@@ -687,7 +687,9 @@ sub new {
 
 sub copy {
   my $self = shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("copy")
+    or return;
 
   unless (defined wantarray) {
     my @caller = caller;
@@ -705,16 +707,19 @@ sub copy {
 sub paste {
   my $self = shift;
 
-  unless ($self->{IMG}) { 
-    $self->_set_error('empty input image');
-    return;
-  }
+  $self->_valid_image("paste")
+    or return;
+
   my %input=(left=>0, top=>0, src_minx => 0, src_miny => 0, @_);
   my $src = $input{img} || $input{src};
   unless($src) {
     $self->_set_error("no source image");
     return;
   }
+  unless ($src->_valid_image("paste")) {
+    $self->{ERRSTR} = $src->{ERRSTR} . " (for src)";
+    return;
+  }
   $input{left}=0 if $input{left} <= 0;
   $input{top}=0 if $input{top} <= 0;
 
@@ -773,7 +778,9 @@ sub paste {
 
 sub crop {
   my $self=shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("crop")
+    or return;
   
   unless (defined wantarray) {
     my @caller = caller;
@@ -861,7 +868,8 @@ sub crop {
 sub _sametype {
   my ($self, %opts) = @_;
 
-  $self->{IMG} or return $self->_set_error("Not a valid image");
+  $self->_valid_image
+    or return;
 
   my $x = $opts{xsize} || $self->getwidth;
   my $y = $opts{ysize} || $self->getheight;
@@ -924,7 +932,9 @@ sub img_set {
 sub masked {
   my $self = shift;
 
-  $self or return undef;
+  $self->_valid_image("masked")
+    or return;
+
   my %opts = (left    => 0, 
               top     => 0, 
               right   => $self->getwidth, 
@@ -965,7 +975,7 @@ sub to_paletted {
     return;
   }
 
-  $self->_valid_image
+  $self->_valid_image("to_paletted")
     or return;
 
   my $result = Imager->new;
@@ -1006,7 +1016,7 @@ sub to_rgb8 {
     return;
   }
 
-  $self->_valid_image
+  $self->_valid_image("to_rgb8")
     or return;
 
   my $result = Imager->new;
@@ -1028,7 +1038,7 @@ sub to_rgb16 {
     return;
   }
 
-  $self->_valid_image
+  $self->_valid_image("to_rgb16")
     or return;
 
   my $result = Imager->new;
@@ -1050,7 +1060,7 @@ sub to_rgb_double {
     return;
   }
 
-  $self->_valid_image
+  $self->_valid_image("to_rgb_double")
     or return;
 
   my $result = Imager->new;
@@ -1066,10 +1076,8 @@ sub addcolors {
   my $self = shift;
   my %opts = (colors=>[], @_);
 
-  unless ($self->{IMG}) {
-    $self->_set_error("empty input image");
-    return;
-  }
+  $self->_valid_image("addcolors")
+    or return -1;
 
   my @colors = @{$opts{colors}}
     or return undef;
@@ -1089,10 +1097,8 @@ sub setcolors {
   my $self = shift;
   my %opts = (start=>0, colors=>[], @_);
 
-  unless ($self->{IMG}) {
-    $self->_set_error("empty input image");
-    return;
-  }
+  $self->_valid_image("setcolors")
+    or return;
 
   my @colors = @{$opts{colors}}
     or return undef;
@@ -1111,6 +1117,10 @@ sub setcolors {
 sub getcolors {
   my $self = shift;
   my %opts = @_;
+
+  $self->_valid_image("getcolors")
+    or return;
+
   if (!exists $opts{start} && !exists $opts{count}) {
     # get them all
     $opts{start} = 0;
@@ -1122,52 +1132,82 @@ sub getcolors {
   elsif (!exists $opts{start}) {
     $opts{start} = 0;
   }
-  
-  $self->{IMG} and 
-    return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
+
+  return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
 }
 
 sub colorcount {
-  i_colorcount($_[0]{IMG});
+  my ($self) = @_;
+
+  $self->_valid_image("colorcount")
+    or return -1;
+
+  return i_colorcount($self->{IMG});
 }
 
 sub maxcolors {
-  i_maxcolors($_[0]{IMG});
+  my $self = shift;
+
+  $self->_valid_image("maxcolors")
+    or return -1;
+
+  i_maxcolors($self->{IMG});
 }
 
 sub findcolor {
   my $self = shift;
   my %opts = @_;
-  $opts{color} or return undef;
 
-  $self->{IMG} and i_findcolor($self->{IMG}, $opts{color});
+  $self->_valid_image("findcolor")
+    or return;
+
+  unless ($opts{color}) {
+    $self->_set_error("findcolor: no color parameter");
+    return;
+  }
+
+  my $color = _color($opts{color})
+    or return;
+
+  return i_findcolor($self->{IMG}, $color);
 }
 
 sub bits {
   my $self = shift;
-  my $bits = $self->{IMG} && i_img_bits($self->{IMG});
+
+  $self->_valid_image("bits")
+    or return;
+
+  my $bits = i_img_bits($self->{IMG});
   if ($bits && $bits == length(pack("d", 1)) * 8) {
     $bits = 'double';
   }
-  $bits;
+  return $bits;
 }
 
 sub type {
   my $self = shift;
-  if ($self->{IMG}) {
-    return i_img_type($self->{IMG}) ? "paletted" : "direct";
-  }
+
+  $self->_valid_image("type")
+    or return;
+
+  return i_img_type($self->{IMG}) ? "paletted" : "direct";
 }
 
 sub virtual {
   my $self = shift;
-  $self->{IMG} and i_img_virtual($self->{IMG});
+
+  $self->_valid_image("virtual")
+    or return;
+
+  return i_img_virtual($self->{IMG});
 }
 
 sub is_bilevel {
   my ($self) = @_;
 
-  $self->{IMG} or return;
+  $self->_valid_image("is_bilevel")
+    or return;
 
   return i_img_is_monochrome($self->{IMG});
 }
@@ -1175,7 +1215,8 @@ sub is_bilevel {
 sub tags {
   my ($self, %opts) = @_;
 
-  $self->{IMG} or return;
+  $self->_valid_image("tags")
+    or return;
 
   if (defined $opts{name}) {
     my @result;
@@ -1211,7 +1252,9 @@ sub addtag {
   my $self = shift;
   my %opts = @_;
 
-  return -1 unless $self->{IMG};
+  $self->_valid_image("addtag")
+    or return;
+
   if ($opts{name}) {
     if (defined $opts{value}) {
       if ($opts{value} =~ /^\d+$/) {
@@ -1259,7 +1302,8 @@ sub deltag {
   my $self = shift;
   my %opts = @_;
 
-  return 0 unless $self->{IMG};
+  $self->_valid_image("deltag")
+    or return 0;
 
   if (defined $opts{'index'}) {
     return i_tags_delete($self->{IMG}, $opts{'index'});
@@ -1279,6 +1323,9 @@ sub deltag {
 sub settag {
   my ($self, %opts) = @_;
 
+  $self->_valid_image("settag")
+    or return;
+
   if ($opts{name}) {
     $self->deltag(name=>$opts{name});
     return $self->addtag(name=>$opts{name}, value=>$opts{value});
@@ -1771,11 +1818,12 @@ sub write {
             fax_fine=>1, @_);
   my $rc;
 
+  $self->_valid_image("write")
+    or return;
+
   $self->_set_opts(\%input, "i_", $self)
     or return undef;
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
-
   my $type = $input{'type'};
   if (!$type and $input{file}) { 
     $type = $FORMATGUESS->($input{file});
@@ -1868,9 +1916,13 @@ sub write_multi {
     return;
   }
   # translate to ImgRaw
-  if (grep !UNIVERSAL::isa($_, 'Imager') || !$_->{IMG}, @images) {
-    $class->_set_error('Usage: Imager->write_multi({ options }, @images)');
-    return 0;
+  my $index = 1;
+  for my $img (@images) {
+    unless ($img->_valid_image("write_multi")) {
+      $class->_set_error($img->errstr . " (image $index)");
+      return;
+    }
+    ++$index;
   }
   $class->_set_opts($opts, "i_", @images)
     or return;
@@ -2004,7 +2056,9 @@ sub filter {
   my $self=shift;
   my %input=@_;
   my %hsh;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("filter")
+    or return;
 
   if (!$input{'type'}) { $self->{ERRSTR}='type parameter missing'; return undef; }
 
@@ -2179,10 +2233,8 @@ sub scale {
     return;
   }
 
-  unless ($self->{IMG}) { 
-    $self->_set_error('empty input image'); 
-    return undef;
-  }
+  $self->_valid_image("scale")
+    or return;
 
   my ($x_scale, $y_scale, $new_width, $new_height) = 
     $self->scale_calculate(%opts)
@@ -2236,10 +2288,8 @@ sub scaleX {
     return;
   }
 
-  unless ($self->{IMG}) { 
-    $self->{ERRSTR} = 'empty input image';
-    return undef;
-  }
+  $self->_valid_image("scaleX")
+    or return;
 
   my $img = Imager->new();
 
@@ -2276,7 +2326,8 @@ sub scaleY {
     return;
   }
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $self->_valid_image("scaleY")
+    or return;
 
   my $img = Imager->new();
 
@@ -2307,13 +2358,15 @@ sub scaleY {
 
 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
 
+  $self->_valid_image("transform")
+    or return;
+
   if ( $opts{'xexpr'} and $opts{'yexpr'} ) {
     if (!$I2P) {
       eval ("use Affix::Infix2Postfix;");
@@ -2405,6 +2458,15 @@ sub transform2 {
   $opts->{variables} = [ qw(x y) ];
   my ($width, $height) = @{$opts}{qw(width height)};
   if (@imgs) {
+    my $index = 1;
+    for my $img (@imgs) {
+      unless ($img->_valid_image("transform2")) {
+       Imager->_set_error($img->errstr . " (input image $index)");
+       return;
+      }
+      ++$index;
+    }
+
     $width ||= $imgs[0]->getwidth();
     $height ||= $imgs[0]->getheight();
     my $img_num = 1;
@@ -2459,13 +2521,12 @@ sub rubthrough {
   my $self=shift;
   my %opts= @_;
 
-  unless ($self->{IMG}) { 
-    $self->{ERRSTR}='empty input image'; 
-    return undef;
-  }
-  unless ($opts{src} && $opts{src}->{IMG}) {
-    $self->{ERRSTR}='empty input image for src'; 
-    return undef;
+  $self->_valid_image("rubthrough")
+    or return;
+
+  unless ($opts{src} && $opts{src}->_valid_image("rubthrough")) {
+    $self->{ERRSTR} = $opts{src}{ERRSTR} . ' (for src)';
+    return;
   }
 
   %opts = (src_minx => 0,
@@ -2502,18 +2563,16 @@ sub compose {
      @_
     );
 
-  unless ($self->{IMG}) {
-    $self->_set_error("compose: empty input image");
-    return;
-  }
+  $self->_valid_image("compose")
+    or return;
 
   unless ($opts{src}) {
     $self->_set_error("compose: src parameter missing");
     return;
   }
   
-  unless ($opts{src}{IMG}) {
-    $self->_set_error("compose: src parameter empty image");
+  unless ($opts{src}->_valid_image("compose")) {
+    $self->_set_error($opts{src}->errstr . " (for src)");
     return;
   }
   my $src = $opts{src};
@@ -2549,8 +2608,8 @@ sub compose {
   my $combine = $self->_combine($opts{combine}, 'normal');
 
   if ($opts{mask}) {
-    unless ($opts{mask}{IMG}) {
-      $self->_set_error("compose: mask parameter empty image");
+    unless ($opts{mask}->_valid_image("compose")) {
+      $self->_set_error($opts{mask}->errstr . " (for mask)");
       return;
     }
 
@@ -2584,6 +2643,10 @@ sub compose {
 sub flip {
   my $self  = shift;
   my %opts  = @_;
+
+  $self->_valid_image("flip")
+    or return;
+
   my %xlate = (h=>0, v=>1, hv=>2, vh=>2);
   my $dir;
   return () unless defined $opts{'dir'} and defined $xlate{$opts{'dir'}};
@@ -2602,6 +2665,9 @@ sub rotate {
     return;
   }
 
+  $self->_valid_image("rotate")
+    or return;
+
   if (defined $opts{right}) {
     my $degrees = $opts{right};
     if ($degrees < 0) {
@@ -2661,6 +2727,9 @@ sub matrix_transform {
   my $self = shift;
   my %opts = @_;
 
+  $self->_valid_image("matrix_transform")
+    or return;
+
   unless (defined wantarray) {
     my @caller = caller;
     warn "copy() called in void context - copy() returns the copied image at $caller[1] line $caller[2]\n";
@@ -2709,10 +2778,8 @@ sub box {
   my $self=shift;
   my $raw = $self->{IMG};
 
-  unless ($raw) {
-    $self->{ERRSTR}='empty input image';
-    return undef;
-  }
+  $self->_valid_image("box")
+    or return;
 
   my %opts = @_;
 
@@ -2790,7 +2857,10 @@ sub box {
 
 sub arc {
   my $self=shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("arc")
+    or return;
+
   my $dflcl= [ 255, 255, 255, 255];
   my $good = 1;
   my %opts=
@@ -2892,7 +2962,9 @@ sub line {
   my %opts=(color=>$dflcl,
            endp => 1,
            @_);
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("line")
+    or return;
 
   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; }
@@ -2923,7 +2995,8 @@ sub polyline {
   my $dflcl=i_color_new(0,0,0,0);
   my %opts=(color=>$dflcl,@_);
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $self->_valid_image("polyline")
+    or return;
 
   if (exists($opts{points})) { @points=@{$opts{points}}; }
   if (!exists($opts{points}) and exists($opts{'x'}) and exists($opts{'y'}) ) {
@@ -2962,7 +3035,8 @@ sub polygon {
   my $dflcl = i_color_new(0,0,0,0);
   my %opts = (color=>$dflcl, @_);
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $self->_valid_image("polygon")
+    or return;
 
   if (exists($opts{points})) {
     $opts{'x'} = [ map { $_->[0] } @{$opts{points}} ];
@@ -3009,7 +3083,8 @@ sub polybezier {
   my $dflcl=i_color_new(0,0,0,0);
   my %opts=(color=>$dflcl,@_);
 
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+  $self->_valid_image("polybezier")
+    or return;
 
   if (exists $opts{points}) {
     $opts{'x'}=map { $_->[0]; } @{$opts{'points'}};
@@ -3035,6 +3110,9 @@ sub flood_fill {
   my %opts = ( color=>Imager::Color->new(255, 255, 255), @_ );
   my $rc;
 
+  $self->_valid_image("flood_fill")
+    or return;
+
   unless (exists $opts{'x'} && exists $opts{'y'}) {
     $self->{ERRSTR} = "missing seed x and y parameters";
     return undef;
@@ -3252,7 +3330,8 @@ sub getscanline {
   my $self = shift;
   my %opts = ( type => '8bit', x=>0, @_);
 
-  $self->_valid_image or return;
+  $self->_valid_image("getscanline")
+    or return;
 
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
@@ -3287,7 +3366,8 @@ sub setscanline {
   my $self = shift;
   my %opts = ( x=>0, @_);
 
-  $self->_valid_image or return;
+  $self->_valid_image("setscanline")
+    or return;
 
   unless (defined $opts{'y'}) {
     $self->_set_error("missing y parameter");
@@ -3348,6 +3428,9 @@ sub getsamples {
   my $self = shift;
   my %opts = ( type => '8bit', x=>0, offset => 0, @_);
 
+  $self->_valid_image("getsamples")
+    or return;
+
   defined $opts{width} or $opts{width} = $self->getwidth - $opts{x};
 
   unless (defined $opts{'y'}) {
@@ -3418,10 +3501,8 @@ sub getsamples {
 sub setsamples {
   my $self = shift;
 
-  unless ($self->{IMG}) {
-    $self->_set_error('setsamples: empty input image');
-    return;
-  }
+  $self->_valid_image("setsamples")
+    or return;
 
   my %opts = ( x => 0, offset => 0 );
   my $data_index;
@@ -3503,6 +3584,9 @@ sub convert {
   my ($self, %opts) = @_;
   my $matrix;
 
+  $self->_valid_image("convert")
+    or return;
+
   unless (defined wantarray) {
     my @caller = caller;
     warn "convert() called in void context - convert() returns the converted image at $caller[1] line $caller[2]\n";
@@ -3621,8 +3705,8 @@ sub combine {
       $class->_set_error("src must contain image objects");
       return;
     }
-    unless ($img->{IMG}) {
-      $class->_set_error("empty input image");
+    unless ($img->_valid_image("combine")) {
+      $Imager::ERRSTR = $img->{ERRSTR} . " (src->[$index])";
       return;
     }
     push @imgs, $img->{IMG};
@@ -3652,6 +3736,9 @@ sub map {
   my ($self, %opts) = @_;
   my @chlist = qw( red green blue alpha );
 
+  $self->_valid_image("map")
+    or return;
+
   if (!exists($opts{'maps'})) {
     # make maps from channel maps
     my $chnum;
@@ -3672,15 +3759,17 @@ sub map {
 sub difference {
   my ($self, %opts) = @_;
 
+  $self->_valid_image("difference")
+    or return;
+
   defined $opts{mindist} or $opts{mindist} = 0;
 
   defined $opts{other}
     or return $self->_set_error("No 'other' parameter supplied");
-  defined $opts{other}{IMG}
-    or return $self->_set_error("No image data in 'other' image");
-
-  $self->{IMG}
-    or return $self->_set_error("No image data");
+  unless ($opts{other}->_valid_image("difference")) {
+    $self->_set_error($opts{other}->errstr . " (other image)");
+    return;
+  }
 
   my $result = Imager->new;
   $result->{IMG} = i_diff_image($self->{IMG}, $opts{other}{IMG}, 
@@ -3704,12 +3793,10 @@ sub border {
 sub getwidth {
   my $self = shift;
 
-  if (my $raw = $self->{IMG}) {
-    return i_img_get_width($raw);
-  }
-  else {
-    $self->{ERRSTR} = 'image is empty'; return undef;
-  }
+  $self->_valid_image("getwidth")
+    or return;
+
+  return i_img_get_width($self->{IMG});
 }
 
 # Get the height of an image
@@ -3717,19 +3804,20 @@ sub getwidth {
 sub getheight {
   my $self = shift;
 
-  if (my $raw = $self->{IMG}) {
-    return i_img_get_height($raw);
-  }
-  else {
-    $self->{ERRSTR} = 'image is empty'; return undef;
-  }
+  $self->_valid_image("getheight")
+    or return;
+
+  return i_img_get_height($self->{IMG});
 }
 
 # Get number of channels in an image
 
 sub getchannels {
   my $self = shift;
-  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+
+  $self->_valid_image("getchannels")
+    or return;
+
   return i_img_getchannels($self->{IMG});
 }
 
@@ -3737,7 +3825,10 @@ sub getchannels {
 
 sub getmask {
   my $self = shift;
-  if (!defined($self->{IMG})) { $self->{ERRSTR} = 'image is empty'; return undef; }
+
+  $self->_valid_image("getmask")
+    or return;
+
   return i_img_getmask($self->{IMG});
 }
 
@@ -3746,14 +3837,15 @@ sub getmask {
 sub setmask {
   my $self = shift;
   my %opts = @_;
-  if (!defined($self->{IMG})) { 
-    $self->{ERRSTR} = 'image is empty';
-    return undef;
-  }
+
+  $self->_valid_image("setmask")
+    or return;
+
   unless (defined $opts{mask}) {
     $self->_set_error("mask parameter required");
     return;
   }
+
   i_img_setmask( $self->{IMG} , $opts{mask} );
 
   1;
@@ -3764,7 +3856,10 @@ sub setmask {
 sub getcolorcount {
   my $self=shift;
   my %opts=('maxcolors'=>2**30,@_);
-  if (!defined($self->{IMG})) { $self->{ERRSTR}='image is empty'; return undef; }
+
+  $self->_valid_image("getcolorcount")
+    or return;
+
   my $rc=i_count_colors($self->{IMG},$opts{'maxcolors'});
   return ($rc==-1? undef : $rc);
 }
@@ -3773,7 +3868,10 @@ sub getcolorcount {
 # values are the number of pixels in this colour.
 sub getcolorusagehash {
   my $self = shift;
-  
+
+  $self->_valid_image("getcolorusagehash")
+    or return;
+
   my %opts = ( maxcolors => 2**30, @_ );
   my $max_colors = $opts{maxcolors};
   unless (defined $max_colors && $max_colors > 0) {
@@ -3781,11 +3879,6 @@ sub getcolorusagehash {
     return;
   }
 
-  unless (defined $self->{IMG}) {
-    $self->_set_error('empty input image'); 
-    return;
-  }
-
   my $channels= $self->getchannels;
   # We don't want to look at the alpha channel, because some gifs using it
   # doesn't define it for every colour (but only for some)
@@ -3809,6 +3902,9 @@ sub getcolorusagehash {
 sub getcolorusage {
   my $self = shift;
 
+  $self->_valid_image("getcolorusage")
+    or return;
+
   my %opts = ( maxcolors => 2**30, @_ );
   my $max_colors = $opts{maxcolors};
   unless (defined $max_colors && $max_colors > 0) {
@@ -3816,11 +3912,6 @@ sub getcolorusage {
     return;
   }
 
-  unless (defined $self->{IMG}) {
-    $self->_set_error('empty input image'); 
-    return undef;
-  }
-
   return i_get_anonymous_color_histo($self->{IMG}, $max_colors);
 }
 
@@ -3828,7 +3919,9 @@ sub getcolorusage {
 
 sub string {
   my $self = shift;
-  unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
+
+  $self->_valid_image("string")
+    or return;
 
   my %input=('x'=>0, 'y'=>0, @_);
   defined($input{string}) or $input{string} = $input{text};
@@ -3855,10 +3948,9 @@ sub align_string {
 
   my $img;
   if (ref $self) {
-    unless ($self->{IMG}) { 
-      $self->{ERRSTR}='empty input image'; 
-      return;
-    }
+    $self->_valid_image("align_string")
+      or return;
+
     $img = $self;
   }
   else {
@@ -4381,6 +4473,10 @@ L<Imager::ExtUtils> - tools to get access to Imager's C API.
 
 L<Imager::Security> - brief security notes.
 
+=item *
+
+L<Imager::Threads> - brief information on working with threads.
+
 =back
 
 =head2 Basic Overview
@@ -4822,6 +4918,8 @@ text, wrapping text in an area - L<Imager::Font::Wrap>
 
 text, measuring - L<Imager::Font/bounding_box()>, L<Imager::Font::BBox>
 
+threads - L<Imager::Threads>
+
 tiles, color - L<Imager::Filters/mosaic>
 
 transparent images - L<Imager::ImageTypes>,
@@ -4835,15 +4933,6 @@ watermark - L<Imager::Filters/watermark>
 
 writing an image to a file - L<Imager::Files>
 
-=head1 THREADS
-
-Imager doesn't support perl threads.
-
-Imager has limited code to prevent double frees if you create images,
-colors etc, and then create a thread, but has no code to prevent two
-threads entering Imager's error handling code, and none is likely to
-be added.
-
 =head1 SUPPORT
 
 The best place to get help with Imager is the mailing list.
index f2e1357..2750520 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -29,6 +29,69 @@ extern "C" {
 
 #include "imperl.h"
 
+/*
+
+Context object management
+
+*/
+
+typedef im_context_t Imager__Context;
+
+#define im_context_DESTROY(ctx) im_context_refdec((ctx), "DESTROY")
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+#define MY_CXT_KEY "Imager::_context" XS_VERSION
+
+typedef struct {
+  im_context_t ctx;
+} my_cxt_t;
+
+START_MY_CXT
+
+im_context_t fallback_context;
+
+static void
+start_context(pTHX) {
+  dMY_CXT;
+  MY_CXT.ctx = im_context_new();
+  sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
+
+  /* Ideally we'd free this reference, but the error message memory
+     was never released on exit, so the associated memory here is reasonable
+     to keep.
+     With logging enabled we always need at least one context, since
+     objects may be released fairly late and attempt to get the log file.
+  */
+  im_context_refinc(MY_CXT.ctx, "start_context");
+  fallback_context = MY_CXT.ctx;
+}
+
+static im_context_t
+perl_get_context(void) {
+  dTHX;
+  dMY_CXT;
+  
+  return MY_CXT.ctx ? MY_CXT.ctx : fallback_context;
+}
+
+#else
+
+static im_context_t perl_context;
+
+static void
+start_context(pTHX) {
+  perl_context = im_context_new();
+  im_context_refinc(perl_context, "start_context");
+}
+
+static im_context_t
+perl_get_context(void) {
+  return perl_context;
+}
+
+#endif
+
 /* used to represent channel lists parameters */
 typedef struct i_channel_list_tag {
   int *channels;
@@ -727,7 +790,6 @@ validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
   }
 }
 
-
 /* I don't think ICLF_* names belong at the C interface
    this makes the XS code think we have them, to let us avoid 
    putting function bodies in the XS code
@@ -2060,7 +2122,7 @@ i_convert(src, avmain)
          RETVAL
 
 
-void
+undef_int
 i_map(im, pmaps)
     Imager::ImgRaw     im
        PREINIT:
@@ -2098,6 +2160,9 @@ i_map(im, pmaps)
           }
           i_map(im, maps, mask);
          myfree(maps);
+         RETVAL = 1;
+    OUTPUT:
+       RETVAL
 
 
 
@@ -3993,6 +4058,37 @@ i_int_hlines_CLONE_SKIP(cls)
 
 #endif
 
+MODULE = Imager  PACKAGE = Imager::Context PREFIX=im_context_
+
+void
+im_context_DESTROY(ctx)
+   Imager::Context ctx
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+void
+im_context_CLONE(...)
+    CODE:
+      MY_CXT_CLONE;
+      (void)items;
+      /* the following sv_setref_pv() will free this inc */
+      im_context_refinc(MY_CXT.ctx, "CLONE");
+      MY_CXT.ctx = im_context_clone(MY_CXT.ctx, "CLONE");
+      sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
+
+#endif
+
 BOOT:
         PERL_SET_GLOBAL_CALLBACKS;
        PERL_PL_SET_GLOBAL_CALLBACKS;
+#ifdef PERL_IMPLICIT_CONTEXT
+       {
+          MY_CXT_INIT;
+         (void)MY_CXT;
+       }
+#endif
+       start_context(aTHX);
+       im_get_context = perl_get_context;
+#ifdef HAVE_LIBTT
+        i_tt_start();
+#endif
index f970330..85f0d9a 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -7,6 +7,7 @@ Changes.old                     Old changes
 color.c                                Color translation and handling
 combine.im                     Channel combine
 compose.im
+context.c
 conv.im
 convert.im
 CountColor/CountColor.pm       sample XS access to API
@@ -49,11 +50,11 @@ Flines/Flines.xs
 Flines/Makefile.PL
 Flines/t/t00flines.t
 flip.im
-font.c
 fontfiles/dodge.ttf
 fontfiles/ExistenceTest.ttf    generated using pfaedit
 fontfiles/ImUgly.ttf
 fontfiles/NameTest.ttf         test glyph_names() - see FT2/t/t10ft2.t
+fontft1.c
 FT2/fontfiles/dodge.ttf
 FT2/fontfiles/ExistenceTest.afm
 FT2/fontfiles/ExistenceTest.pfb
@@ -68,6 +69,7 @@ FT2/imft2.h
 FT2/Makefile.PL
 FT2/README
 FT2/t/t10ft2.t
+FT2/t/t20thread.t
 FT2/typemap
 gaussian.im
 GIF/GIF.pm
@@ -183,6 +185,7 @@ lib/Imager/Font.pm
 lib/Imager/Font/BBox.pm
 lib/Imager/Font/FreeType2.pm
 lib/Imager/Font/Image.pm
+lib/Imager/Font/Test.pm                Font for testing (outputs boxes only)
 lib/Imager/Font/Truetype.pm
 lib/Imager/Font/Type1.pm       Compatibility wrapper for Imager::Font::T1
 lib/Imager/Font/Wrap.pm
@@ -200,6 +203,7 @@ lib/Imager/regmach.pod
 lib/Imager/Regops.pm
 lib/Imager/Security.pod
 lib/Imager/Test.pm
+lib/Imager/Threads.pod
 lib/Imager/Transform.pm
 lib/Imager/Transformations.pod
 lib/Imager/Tutorial.pod
@@ -216,6 +220,9 @@ MANIFEST
 MANIFEST.SKIP
 map.c
 maskimg.c
+mutexnull.c
+mutexpthr.c
+mutexwin.c
 palimg.c
 paste.im
 plug.h
@@ -311,6 +318,9 @@ t/t023palette.t                     Test paletted images
 t/t03test.t                    Test Imager::Test
 t/t05error.t
 t/t07iolayer.t
+t/t080log.t
+t/t081error.t
+t/t082limit.t
 t/t1000files.t                 Format independent file tests
 t/t1000lib/Imager/File/BAD.pm  Test failing to load a file handler
 t/t101nojpeg.t                 Test handling when jpeg not available
@@ -350,6 +360,7 @@ t/t80texttools.t            Test text wrapping
 t/t81hlines.t                  Test hlines.c
 t/t82inline.t                  Test Inline::C integration
 t/t83extutil.t                 Test Imager::ExtUtils
+t/t84inlinectx.t
 t/t90cc.t
 t/t91pod.t                     Test POD with Test::Pod
 t/t92samples.t
@@ -372,8 +383,10 @@ T1/Makefile.PL
 T1/README
 T1/t/t10type1.t
 T1/t/t20oo.t
+T1/t/t30thread.t
 T1/T1.pm
 T1/T1.xs
+T1/typemap
 tags.c
 testimg/alpha16.tga            16-bit/pixel TGA with alpha "channel" RT 32926
 testimg/bad1oflow.bmp          1-bit/pixel, overflow integer on 32-bit machines
index ef04e5d..b1d5a89 100644 (file)
@@ -134,3 +134,4 @@ Makefile\.old$
 
 # sub-module build junk
 \.bak$
+MYMETA.json
\ No newline at end of file
index 0f7a98b..3d3cffa 100644 (file)
@@ -48,6 +48,7 @@ my @incpaths; # places to look for headers
 my @libpaths; # places to look for libraries
 my $coverage; # build for coverage testing
 my $assert; # build with assertions
+my $trace_context; # trace context management to stderr
 GetOptions("help" => \$help,
            "enable=s" => \@enable,
            "disable=s" => \@disable,
@@ -56,7 +57,8 @@ GetOptions("help" => \$help,
            "verbose|v" => \$VERBOSE,
            "nolog" => \$NOLOG,
           'coverage' => \$coverage,
-          "assert|a" => \$assert);
+          "assert|a" => \$assert,
+          "tracecontext" => \$trace_context);
 
 setenv();
 
@@ -159,19 +161,45 @@ my $OSDEF  = "-DOS_$^O";
 if ($^O eq 'hpux')                { $OSLIBS .= ' -ldld'; }
 if (defined $Config{'d_dlsymun'}) { $OSDEF  .= ' -DDLSYMUN'; }
 
-my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
-              log.o gaussian.o conv.o pnm.o raw.o feat.o font.o combine.o
+my @objs = qw(Imager.o context.o draw.o polygon.o image.o io.o iolayer.o
+              log.o gaussian.o conv.o pnm.o raw.o feat.o combine.o
               filters.o dynaload.o stackmach.o datatypes.o
               regmach.o trans2.o quant.o error.o convert.o
               map.o tags.o palimg.o maskimg.o img8.o img16.o rotate.o
               bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
               imext.o scale.o rubthru.o render.o paste.o compose.o flip.o);
 
+if ($Config{useithreads}) {
+  if ($Config{i_pthread}) {
+    print "POSIX threads\n";
+    push @objs, "mutexpthr.o";
+  }
+  elsif ($^O eq 'MSWin32') {
+    print "Win32 threads\n";
+    push @objs, "mutexwin.o";
+  }
+  else {
+    print "Unsupported threading model\n";
+    push @objs, "mutexnull.o";
+    if ($ENV{AUTOMATED_TESTING}) {
+      die "OS unsupported: no threading support code for this platform\n";
+    }
+  }
+}
+else {
+  print "No threads\n";
+  push @objs, "mutexnull.o";
+}
+
 my @typemaps = qw(typemap.local typemap);
 if ($] < 5.008) {
     unshift @typemaps, "typemap.oldperl";
 }
 
+if ($trace_context) {
+  $CFLAGS .= " -DIMAGER_TRACE_CONTEXT";
+}
+
 my %opts=
   (
    'NAME'         => 'Imager',
@@ -521,7 +549,7 @@ sub init {
                        && !-e catfile($_[0], 'fterrors.h') },
      libcheck=>sub { $_[0] eq "libttf$aext" or $_[0] eq "libttf.$lext" },
      libfiles=>'-lttf',
-     objfiles=>'',
+     objfiles=>'fontft1.o',
      code => \&freetype1_probe,
      docs=>q{
 Truetype fonts are scalable fonts. They can include 
index d0b444f..edb0b58 100644 (file)
--- a/T1/T1.pm
+++ b/T1/T1.pm
@@ -3,6 +3,7 @@ use strict;
 use Imager::Color;
 use vars qw(@ISA $VERSION);
 @ISA = qw(Imager::Font);
+use Scalar::Util ();
 
 BEGIN {
   $VERSION = "1.019";
@@ -14,17 +15,7 @@ BEGIN {
 
 *_first = \&Imager::Font::_first;
 
-my $t1aa;
-
-# $T1AA is in there because for some reason (probably cache related) antialiasing
-# is a system wide setting in t1 lib.
-
-sub t1_set_aa_level {
-  if (!defined $t1aa or $_[0] != $t1aa) {
-    i_t1_set_aa($_[0]);
-    $t1aa=$_[0];
-  }
-}
+my $t1aa = 2;
 
 sub new {
   my $class = shift;
@@ -65,39 +56,46 @@ sub new {
          $hsh{afm} = 0;
   }
 
-  my $id = i_t1_new($hsh{file},$hsh{afm});
-  unless ($id >= 0) { # the low-level code may miss some error handling
+  my $font = Imager::Font::T1xs->new($hsh{file},$hsh{afm});
+  unless ($font) { # the low-level code may miss some error handling
     Imager->_set_error(Imager->_error_as_msg);
     return;
   }
   return bless {
-               id    => $id,
+               t1font    => $font,
                aa    => $hsh{aa} || 0,
                file  => $hsh{file},
                type  => 't1',
                size  => $hsh{size},
                color => $hsh{color},
+               t1aa  => $t1aa,
               }, $class;
 }
 
 sub _draw {
   my $self = shift;
+
+  $self->_valid
+    or return;
+
   my %input = @_;
-  t1_set_aa_level($input{aa});
   my $flags = '';
   $flags .= 'u' if $input{underline};
   $flags .= 's' if $input{strikethrough};
   $flags .= 'o' if $input{overline};
+  my $aa = $input{aa} ? $self->{t1aa} : 0;
   if (exists $input{channel}) {
-    i_t1_cp($input{image}{IMG}, $input{'x'}, $input{'y'},
-                   $input{channel}, $self->{id}, $input{size},
+    $self->{t1font}->cp($input{image}{IMG}, $input{'x'}, $input{'y'},
+                   $input{channel}, $input{size},
                    $input{string}, length($input{string}), $input{align},
-                    $input{utf8}, $flags);
+                    $input{utf8}, $flags, $aa)
+      or return;
   } else {
-    i_t1_text($input{image}{IMG}, $input{'x'}, $input{'y'}, 
-                     $input{color}, $self->{id}, $input{size}, 
+    $self->{t1font}->text($input{image}{IMG}, $input{'x'}, $input{'y'}, 
+                     $input{color}, $input{size}, 
                      $input{string}, length($input{string}), 
-                     $input{align}, $input{utf8}, $flags);
+                     $input{align}, $input{utf8}, $flags, $aa)
+      or return;
   }
 
   return $self;
@@ -105,12 +103,16 @@ sub _draw {
 
 sub _bounding_box {
   my $self = shift;
+
+  $self->_valid
+    or return;
+
   my %input = @_;
   my $flags = '';
   $flags .= 'u' if $input{underline};
   $flags .= 's' if $input{strikethrough};
   $flags .= 'o' if $input{overline};
-  return i_t1_bbox($self->{id}, $input{size}, $input{string},
+  return $self->{t1font}->bbox($input{size}, $input{string},
                           length($input{string}), $input{utf8}, $flags);
 }
 
@@ -118,12 +120,15 @@ sub _bounding_box {
 sub has_chars {
   my ($self, %hsh) = @_;
 
+  $self->_valid
+    or return;
+
   unless (defined $hsh{string} && length $hsh{string}) {
     $Imager::ERRSTR = "No string supplied to \$font->has_chars()";
     return;
   }
-  return i_t1_has_chars($self->{id}, $hsh{string}, 
-                               _first($hsh{'utf8'}, $self->{utf8}, 0));
+  return $self->{t1font}->has_chars($hsh{string}, 
+                                   _first($hsh{'utf8'}, $self->{utf8}, 0));
 }
 
 sub utf8 {
@@ -133,20 +138,58 @@ sub utf8 {
 sub face_name {
   my ($self) = @_;
 
-  i_t1_face_name($self->{id});
+  $self->_valid
+    or return;
+
+  return $self->{t1font}->face_name();
 }
 
 sub glyph_names {
   my ($self, %input) = @_;
 
+  $self->_valid
+    or return;
+
   my $string = $input{string};
   defined $string
     or return Imager->_set_error("no string parameter passed to glyph_names");
   my $utf8 = _first($input{utf8} || 0);
 
-  i_t1_glyph_name($self->{id}, $string, $utf8);
+  return $self->{t1font}->glyph_name($string, $utf8);
+}
+
+sub set_aa_level {
+  my ($self, $new_t1aa) = @_;
+
+  if (!defined $new_t1aa ||
+      ($new_t1aa != 1 && $new_t1aa != 2)) {
+    Imager->_set_error("set_aa_level: parameter must be 1 or 2");
+    return;
+  }
+
+  if (ref $self) {
+    $self->_valid
+      or return;
+
+    $self->{t1aa} = $new_t1aa;
+  }
+  else {
+    $t1aa = $new_t1aa;
+  }
+
+  return 1;
 }
 
+sub _valid {
+  my $self = shift;
+
+  unless ($self->{t1font} && Scalar::Util::blessed($self->{t1font})) {
+    Imager->_set_error("font object was created in another thread");
+    return;
+  }
+
+  return 1;
+}
 
 1;
 
@@ -197,6 +240,32 @@ C<strikethrough> - Draw the text with a strikethrough.
 Obviously, if you're calculating the bounding box the size of the line
 is included in the box, and the line isn't drawn :)
 
+=head2 Anti-aliasing
+
+T1Lib supports multiple levels of anti-aliasing, by default, if you
+request anti-aliased output, Imager::Font::T1 will use the maximum
+level.
+
+You can override this with the set_t1_aa() method:
+
+=over
+
+=item set_aa_level()
+
+Usage:
+
+  $font->set_aa_level(1);
+  Imager::Font::T1->set_aa_level(2);
+
+Sets the T1Lib anti-aliasing level either for the specified font, or
+for new font objects.
+
+The only parameter must be 1 or 2.
+
+Returns true on success.
+
+=back
+
 =head1 AUTHOR
 
 Addi, Tony
index f587fb0..6c973ea 100644 (file)
--- a/T1/T1.xs
+++ b/T1/T1.xs
@@ -11,38 +11,43 @@ extern "C" {
 
 DEFINE_IMAGER_CALLBACKS;
 
+typedef i_t1_font_t Imager__Font__T1xs;
+
+#define i_t1_DESTROY(font) i_t1_destroy(font)
+
 MODULE = Imager::Font::T1  PACKAGE = Imager::Font::T1
 
 undef_int
 i_init_t1(t1log)
        int t1log
 
-void
-i_t1_set_aa(st)
-              int     st
+MODULE = Imager::Font::T1  PACKAGE = Imager::Font::T1xs PREFIX = i_t1_
 
-int
-i_t1_new(pfb,afm)
+Imager::Font::T1xs
+i_t1_new(class,pfb,afm)
                      char*    pfb
                      char*    afm
+  C_ARGS:
+    pfb, afm
 
-int
-i_t1_destroy(font_id)
-                      int     font_id
+void
+i_t1_DESTROY(font)
+ Imager::Font::T1xs font       
 
 
 undef_int
-i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="")
+i_t1_cp(font,im,xb,yb,channel,points,str_sv, length(str),align,utf8=0,flags="",aa=1)
+ Imager::Font::T1xs     font
     Imager::ImgRaw     im
         i_img_dim     xb
         i_img_dim     yb
               int     channel
-              int     fontnum
             double     points
                SV*    str_sv
               int     align
                int     utf8
               char*    flags
+              int     aa
              PREINIT:
                char *str;
                STRLEN len;
@@ -52,15 +57,15 @@ i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags=""
                  utf8 = 1;
 #endif
                str = SvPV(str_sv, len);
-               RETVAL = i_t1_cp(im, xb,yb,channel,fontnum,points,str,len,align,
-                                  utf8,flags);
+               RETVAL = i_t1_cp(font, im, xb,yb,channel,points,str,len,align,
+                                  utf8,flags,aa);
            OUTPUT:
              RETVAL
 
 
 void
 i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="")
              int     fontnum
Imager::Font::T1xs     fontnum
            double     point
                SV*    str_sv
                int     utf8
@@ -87,17 +92,18 @@ i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="")
 
 
 undef_int
-i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="")
+i_t1_text(font,im,xb,yb,cl,points,str_sv,length(str),align,utf8=0,flags="",aa=1)
+ Imager::Font::T1xs font
     Imager::ImgRaw     im
         i_img_dim     xb
         i_img_dim     yb
      Imager::Color    cl
-              int     fontnum
             double     points
                SV*    str_sv
               int     align
                int     utf8
-              char*    flags
+        const char*    flags
+              int     aa
              PREINIT:
                char *str;
                STRLEN len;
@@ -107,14 +113,14 @@ i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="")
                  utf8 = 1;
 #endif
                str = SvPV(str_sv, len);
-               RETVAL = i_t1_text(im, xb,yb,cl,fontnum,points,str,len,align,
-                                  utf8,flags);
+               RETVAL = i_t1_text(font,im, xb,yb,cl,points,str,len,align,
+                                  utf8,flags,aa);
            OUTPUT:
              RETVAL
 
 void
-i_t1_has_chars(handle, text_sv, utf8 = 0)
-        int handle
+i_t1_has_chars(font, text_sv, utf8 = 0)
+ Imager::Font::T1xs font
         SV  *text_sv
         int utf8
       PREINIT:
@@ -130,9 +136,10 @@ i_t1_has_chars(handle, text_sv, utf8 = 0)
 #endif
         text = SvPV(text_sv, len);
         work = mymalloc(len);
-        count = i_t1_has_chars(handle, text, len, utf8, work);
+        count = i_t1_has_chars(font, text, len, utf8, work);
         if (GIMME_V == G_ARRAY) {
           EXTEND(SP, count);
+
           for (i = 0; i < count; ++i) {
             PUSHs(boolSV(work[i]));
           }
@@ -144,21 +151,21 @@ i_t1_has_chars(handle, text_sv, utf8 = 0)
         myfree(work);
 
 void
-i_t1_face_name(handle)
-        int handle
+i_t1_face_name(font)
+ Imager::Font::T1xs font
       PREINIT:
         char name[255];
         int len;
       PPCODE:
-        len = i_t1_face_name(handle, name, sizeof(name));
+        len = i_t1_face_name(font, name, sizeof(name));
         if (len) {
           EXTEND(SP, 1);
           PUSHs(sv_2mortal(newSVpv(name, strlen(name))));
         }
 
 void
-i_t1_glyph_name(handle, text_sv, utf8 = 0)
-        int handle
+i_t1_glyph_name(font, text_sv, utf8 = 0)
+ Imager::Font::T1xs font
         SV *text_sv
         int utf8
       PREINIT:
@@ -187,7 +194,7 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
             --len;
           }
           EXTEND(SP, 1);
-          if (i_t1_glyph_name(handle, ch, name, sizeof(name))) {
+          if (i_t1_glyph_name(font, ch, name, sizeof(name))) {
             PUSHs(sv_2mortal(newSVpv(name, 0)));
           }
           else {
@@ -195,5 +202,14 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
           } 
         }
 
+int
+i_t1_CLONE_SKIP(...)
+    CODE:
+       (void)items; /* avoid unused warning */
+       RETVAL = 1;
+    OUTPUT:
+       RETVAL
+
 BOOT:
        PERL_INITIALIZE_IMAGER_CALLBACKS;
+       i_t1_start();
\ No newline at end of file
index d67f5fd..d0686bb 100644 (file)
--- a/T1/imt1.c
+++ b/T1/imt1.c
@@ -5,11 +5,33 @@
 
 static int t1_get_flags(char const *flags);
 static char *t1_from_utf8(char const *in, size_t len, int *outlen);
-
+static undef_int i_init_t1_low(int t1log);
 static void t1_push_error(void);
+static void i_t1_set_aa(int st);
 
 static int t1_active_fonts = 0;
 static int t1_initialized = 0;
+static int t1_aa = 0;
+
+struct i_t1_font_tag {
+  int font_id;
+};
+
+static i_mutex_t mutex;
+
+/*
+=item i_t1_start()
+
+Initialize the font driver.  This does not actually initialize T1Lib,
+it just allocates the mutex we use to gate access to it.
+
+=cut
+*/
+
+void
+i_t1_start(void) {
+  mutex = i_mutex_new();
+}
 
 /* 
 =item i_init_t1(t1log)
@@ -21,8 +43,21 @@ Initializes the t1lib font rendering engine.
 
 undef_int
 i_init_t1(int t1log) {
+  undef_int result;
+  i_mutex_lock(mutex);
+
+  result = i_init_t1_low(t1log);
+
+  i_mutex_unlock(mutex);
+
+  return result;
+}
+
+static undef_int
+i_init_t1_low(int t1log) {
   int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE;
-  mm_log((1,"init_t1()\n"));
+
+  mm_log((1,"init_t1(%d)\n", t1log));
 
   i_clear_error();
 
@@ -45,7 +80,6 @@ i_init_t1(int t1log) {
     return(1);
   }
   T1_SetLogLevel(T1LOG_DEBUG);
-  i_t1_set_aa(1); /* Default Antialias value */
 
   ++t1_initialized;
 
@@ -64,8 +98,10 @@ Shuts the t1lib font rendering engine down.
 
 void
 i_close_t1(void) {
+  i_mutex_lock(mutex);
   T1_CloseLib();
   t1_initialized = 0;
+  i_mutex_unlock(mutex);
 }
 
 
@@ -80,21 +116,27 @@ Loads the fonts with the given filenames, returns its font id
 =cut
 */
 
-int
+i_t1_font_t
 i_t1_new(char *pfb,char *afm) {
   int font_id;
+  i_t1_font_t font;
+
+  i_mutex_lock(mutex);
 
   i_clear_error();
 
-  if (!t1_initialized && i_init_t1(0))
-    return -1;
+  if (!t1_initialized && i_init_t1_low(0)) {
+    i_mutex_unlock(mutex);
+    return NULL;
+  }
 
   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));
     t1_push_error();
-    return font_id;
+    i_mutex_unlock(mutex);
+    return NULL;
   }
   
   if (afm != NULL) {
@@ -107,33 +149,48 @@ i_t1_new(char *pfb,char *afm) {
     t1_push_error();
     i_push_error(0, "loading font");
     T1_DeleteFont(font_id);
-    return -1;
+    i_mutex_unlock(mutex);
+    return NULL;
   }
 
   ++t1_active_fonts;
 
-  mm_log((1, "i_t1_new() -> %d\n", font_id));
+  i_mutex_unlock(mutex);
+
+  font = mymalloc(sizeof(*font));
+  font->font_id = font_id;
 
-  return font_id;
+  mm_log((1, "i_t1_new() -> %p (%d)\n", font, font_id));
+
+  return font;
 }
 
 /*
-=item i_t1_destroy(font_id)
+=item i_t1_destroy(font)
 
 Frees resources for a t1 font with given font id.
 
-   font_id - number of the font to free
+   font - font to free
 
 =cut
 */
 
 int
-i_t1_destroy(int font_id) {
-  mm_log((1,"i_t1_destroy(font_id %d)\n",font_id));
+i_t1_destroy(i_t1_font_t font) {
+  int result;
+
+  i_mutex_lock(mutex);
+
+  mm_log((1,"i_t1_destroy(font %p (%d))\n", font, font->font_id));
 
   --t1_active_fonts;
 
-  return T1_DeleteFont(font_id);
+  result = T1_DeleteFont(font->font_id);
+  myfree(font);
+
+  i_mutex_unlock(mutex);
+
+  return result;
 }
 
 
@@ -144,13 +201,19 @@ Sets the antialiasing level of the t1 library.
 
    st - 0 =  NONE, 1 = LOW, 2 =  HIGH.
 
+Must be called with the mutex locked.
+
 =cut
 */
 
-void
+static void
 i_t1_set_aa(int st) {
   int i;
   unsigned long cst[17];
+
+  if (t1_aa == st)
+    return;
+
   switch(st) {
   case 0:
     T1_AASetBitsPerPixel( 8 );
@@ -171,11 +234,13 @@ i_t1_set_aa(int st) {
     T1_AAHSetGrayValues( cst );
     mm_log((1,"setting T1 antialias to high\n"));
   }
+  
+  t1_aa = st;
 }
 
 
 /* 
-=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align)
+=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align,aa)
 
 Interface to text rendering into a single channel in an image
 
@@ -188,20 +253,35 @@ Interface to text rendering into a single channel in an image
    str     - string to render
    len     - string length
    align   - (0 - top of font glyph | 1 - baseline )
+   aa      - anti-aliasing level
 
 =cut
 */
 
 undef_int
-i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double points,char* str,size_t len,int align, int utf8, char const *flags) {
+i_t1_cp(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char* str,size_t len,int align, int utf8, char const *flags, int aa) {
   GLYPH *glyph;
   int xsize,ysize,x,y;
   i_color val;
   int mod_flags = t1_get_flags(flags);
+  int fontnum = font->font_id;
 
   unsigned int ch_mask_store;
   
-  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
+  i_clear_error();
+
+  mm_log((1, "i_t1_cp(font %p (%d), im %p, (xb,yb)=" i_DFp ", channel %d, points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n",
+         font, fontnum, im, i_DFcp(xb, yb), channel, points, str, (unsigned)len, align, utf8, flags, aa));
+
+  if (im == NULL) {
+    mm_log((1,"i_t1_cp: Null image in input\n"));
+    i_push_error(0, "null image");
+    return(0);
+  }
+
+  i_mutex_lock(mutex);
+
+  i_t1_set_aa(aa);
 
   if (utf8) {
     int worklen;
@@ -212,8 +292,12 @@ i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double point
   else {
     glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL);
   }
-  if (glyph == NULL)
+  if (glyph == NULL) {
+    t1_push_error();
+    i_push_error(0, "i_t1_cp: T1_AASetString failed");
+    i_mutex_unlock(mutex);
     return 0;
+  }
 
   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));
@@ -236,6 +320,9 @@ i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double point
   }
   
   im->ch_mask=ch_mask_store;
+
+  i_mutex_unlock(mutex);
+
   return 1;
 }
 
@@ -267,14 +354,19 @@ function to get a strings bounding box given the font id and sizes
 */
 
 int
-i_t1_bbox(int fontnum, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) {
+i_t1_bbox(i_t1_font_t font, double points,const char *str,size_t len, i_img_dim cords[6], int utf8,char const *flags) {
   BBox bbox;
   BBox gbbox;
   int mod_flags = t1_get_flags(flags);
   i_img_dim advance;
-  int space_position = T1_GetEncodingIndex(fontnum, "space");
+  int fontnum = font->font_id;
+  int space_position;
+
+  i_mutex_lock(mutex);
+
+  space_position = T1_GetEncodingIndex(fontnum, "space");
   
-  mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len));
+  mm_log((1,"i_t1_bbox(font %p (%d),points %.2f,str '%.*s', len %d)\n",font, fontnum,points,len,str,len));
   T1_LoadFont(fontnum);  /* FIXME: Here a return code is ignored - haw haw haw */ 
 
   if (len == 0) {
@@ -322,12 +414,14 @@ i_t1_bbox(int fontnum, double points,const char *str,size_t len, i_img_dim cords
   cords[BBOX_RIGHT_BEARING] = 
     cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH];
 
+  i_mutex_unlock(mutex);
+
   return BBOX_RIGHT_BEARING+1;
 }
 
 
 /*
-=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align)
+=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align, aa)
 
 Interface to text rendering in a single color onto an image
 
@@ -340,18 +434,33 @@ Interface to text rendering in a single color onto an image
    str     - char pointer to string to render
    len     - string length
    align   - (0 - top of font glyph | 1 - baseline )
+   aa      - anti-aliasing level
 
 =cut
 */
 
 undef_int
-i_t1_text(i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl,int fontnum, double points,const char* str,size_t len,int align, int utf8, char const *flags) {
+i_t1_text(i_t1_font_t font, i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl, double points,const char* str,size_t len,int align, int utf8, char const *flags, int aa) {
   GLYPH *glyph;
   int xsize,ysize,y;
   int mod_flags = t1_get_flags(flags);
   i_render *r;
+  int fontnum = font->font_id;
+
+  mm_log((1, "i_t1_text(font %p (%d), im %p, (xb,yb)=" i_DFp ", cl (%d,%d,%d,%d), points %g, str %p, len %u, align %d, utf8 %d, flags '%s', aa %d)\n",
+         font, fontnum, im, i_DFcp(xb, yb), cl->rgba.r, cl->rgba.g, cl->rgba.b, cl->rgba.a, points, str, (unsigned)len, align, utf8, flags, aa));
+
+  i_clear_error();
+
+  if (im == NULL) {
+    i_push_error(0, "null image");
+    mm_log((1,"i_t1_text: Null image in input\n"));
+    return(0);
+  }
 
-  if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
+  i_mutex_lock(mutex);
+
+  i_t1_set_aa(aa);
 
   if (utf8) {
     int worklen;
@@ -363,8 +472,13 @@ i_t1_text(i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl,int fontnum, d
     /* T1_AASetString() accepts a char * not a const char */
     glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL);
   }
-  if (glyph == NULL)
+  if (glyph == NULL) {
+    mm_log((1, "T1_AASetString failed\n"));
+    t1_push_error();
+    i_push_error(0, "i_t1_text(): T1_AASetString failed");
+    i_mutex_unlock(mutex);
     return 0;
+  }
 
   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));
@@ -383,6 +497,8 @@ i_t1_text(i_img *im, i_img_dim xb, i_img_dim yb,const i_color *cl,int fontnum, d
     i_render_color(r, xb, yb+y, xsize, (unsigned char *)glyph->bits+y*xsize, cl);
   }
   i_render_delete(r);
+
+  i_mutex_unlock(mutex);
     
   return 1;
 }
@@ -467,16 +583,20 @@ Returns the number of characters that were checked.
 */
 
 int
-i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
+i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8,
                char *out) {
   int count = 0;
+  int font_num = font->font_id;
   
+  i_mutex_lock(mutex);
+
   mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", 
           font_num, text, len, utf8));
 
   i_clear_error();
   if (T1_LoadFont(font_num)) {
     t1_push_error();
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -486,6 +606,7 @@ i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
       c = i_utf8_advance(&text, &len);
       if (c == ~0UL) {
         i_push_error(0, "invalid UTF8 character");
+       i_mutex_unlock(mutex);
         return 0;
       }
     }
@@ -512,11 +633,13 @@ i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
     ++count;
   }
 
+  i_mutex_unlock(mutex);
+
   return count;
 }
 
 /*
-=item i_t1_face_name(font_num, name_buf, name_buf_size)
+=item i_t1_face_name(font, name_buf, name_buf_size)
 
 Copies the face name of the given C<font_num> to C<name_buf>.  Returns
 the number of characters required to store the name (which can be
@@ -530,53 +653,68 @@ will be truncated.  name_buf will always be NUL termintaed.
 */
 
 int
-i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) {
+i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size) {
   char *name;
+  int font_num = font->font_id;
+
+  i_mutex_lock(mutex);
 
   T1_errno = 0;
   if (T1_LoadFont(font_num)) {
     t1_push_error();
+    i_mutex_unlock(mutex);
     return 0;
   }
   name = T1_GetFontName(font_num);
 
   if (name) {
+    size_t len = strlen(name);
     strncpy(name_buf, name, name_buf_size);
     name_buf[name_buf_size-1] = '\0';
-    return strlen(name) + 1;
+    i_mutex_unlock(mutex);
+    return len + 1;
   }
   else {
     t1_push_error();
+    i_mutex_unlock(mutex);
     return 0;
   }
 }
 
 int
-i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
+i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf, 
                  size_t name_buf_size) {
   char *name;
+  int font_num = font->font_id;
 
+  i_mutex_lock(mutex);
   i_clear_error();
   if (ch > 0xFF) {
+    i_mutex_unlock(mutex);
     return 0;
   }
   if (T1_LoadFont(font_num)) {
     t1_push_error();
+    i_mutex_unlock(mutex);
     return 0;
   }
   name = T1_GetCharName(font_num, (unsigned char)ch);
   if (name) {
     if (strcmp(name, ".notdef")) {
+      size_t len = strlen(name);
       strncpy(name_buf, name, name_buf_size);
       name_buf[name_buf_size-1] = '\0';
-      return strlen(name) + 1;
+      i_mutex_unlock(mutex);
+      return len + 1;
     }
     else {
+      i_mutex_unlock(mutex);
       return 0;
     }
   }
   else {
     t1_push_error();
+    i_mutex_unlock(mutex);
     return 0;
   }
 }
index 489bcef..5783e05 100644 (file)
--- a/T1/imt1.h
+++ b/T1/imt1.h
@@ -3,38 +3,40 @@
 
 #include "imdatatypes.h"
 
+typedef struct i_t1_font_tag *i_t1_font_t;
+
+extern void
+i_t1_start(void);
+
 extern undef_int
 i_init_t1(int t1log);
 
 extern void
 i_close_t1(void);
 
-extern int
+extern i_t1_font_t
 i_t1_new(char *pfb,char *afm);
 
 extern int
-i_t1_destroy(int font_id);
-
-extern void
-i_t1_set_aa(int st);
+i_t1_destroy(i_t1_font_t font);
 
 extern undef_int
-i_t1_cp(i_img *im,i_img_dim xb,i_img_dim yb,int channel,int fontnum,double points,char* str,size_t len,int align, int utf8, char const *flags);
+i_t1_cp(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char* str,size_t len,int align, int utf8, char const *flags, int aa);
 
 extern int
-i_t1_bbox(int fontnum,double points,const char *str,size_t len,i_img_dim *cords, int utf8,char const *flags);
+i_t1_bbox(i_t1_font_t font,double points,const char *str,size_t len,i_img_dim *cords, int utf8,char const *flags);
 
 extern undef_int
-i_t1_text(i_img *im,i_img_dim xb,i_img_dim yb,const i_color *cl,int fontnum,double points,const char* str,size_t len,int align, int utf8, char const *flags);
+i_t1_text(i_t1_font_t font, i_img *im,i_img_dim xb,i_img_dim yb,const i_color *cl,double points,const char* str,size_t len,int align, int utf8, char const *flags, int aa);
 
 extern int
-i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
+i_t1_has_chars(i_t1_font_t font, const char *text, size_t len, int utf8,
                char *out);
 
 extern int
-i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size);
+i_t1_face_name(i_t1_font_t font, char *name_buf, size_t name_buf_size);
 
 extern int
-i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
+i_t1_glyph_name(i_t1_font_t font, unsigned long ch, char *name_buf, 
                size_t name_buf_size);
 #endif
index 47ea7b0..8d9be3b 100644 (file)
@@ -2,13 +2,13 @@
 use strict;
 use Test::More;
 use Imager ':all';
-use Imager::Test qw(diff_text_with_nul is_color3);
+use Imager::Test qw(diff_text_with_nul is_color3 is_image isnt_image);
 use Imager::Font::T1;
 use Cwd qw(getcwd abs_path);
 
 #$Imager::DEBUG=1;
 
-plan tests => 97;
+plan tests => 108;
 
 ok($Imager::formats{t1}, "must have t1");
 
@@ -41,7 +41,7 @@ SKIP:
   init(t1log=>0);
   unlink "t1lib.log";
 
-  my $fnum=Imager::Font::T1::i_t1_new($fontname_pfb,$fontname_afm); # this will load the pfb font
+  my $fnum=Imager::Font::T1xs->new($fontname_pfb,$fontname_afm); # this will load the pfb font
   unless (ok($fnum >= 0, "load font $fontname_pfb")) {
     skip("without the font I can't do a thing", 90);
   }
@@ -49,11 +49,11 @@ SKIP:
   my $bgcolor=Imager::Color->new(255,0,0,0);
   my $overlay=Imager::ImgRaw::new(200,70,3);
   
-  ok(Imager::Font::T1::i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1), "i_t1_cp");
+  ok($fnum->cp($overlay,5,50,1,50.0,'XMCLH',5,1), "i_t1_cp");
 
   i_line($overlay,0,50,100,50,$bgcolor,1);
 
-  my @bbox=Imager::Font::T1::i_t1_bbox(0,50.0,'XMCLH',5);
+  my @bbox=$fnum->bbox(50.0,'XMCLH',5);
   is(@bbox, 8, "i_t1_bbox");
   print "# bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
 
@@ -66,8 +66,7 @@ SKIP:
   $bgcolor=Imager::Color::set($bgcolor,200,200,200,0);
   my $backgr=Imager::ImgRaw::new(280,300,3);
 
-  Imager::Font::T1::i_t1_set_aa(2);
-  ok(Imager::Font::T1::i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1), "i_t1_text");
+  ok($fnum->text($backgr,10,100,$bgcolor,150.0,'test',4,1,2), "i_t1_text");
 
   # "UTF8" tests
   # for perl < 5.6 we can hand-encode text
@@ -78,9 +77,9 @@ SKIP:
   my $text = pack("C*", 0x41, 0xC2, 0xA1, 0xE2, 0x80, 0x90, 0x41);
   my $alttext = "A\xA1A";
   
-  my @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 1);
+  my @utf8box = $fnum->bbox(50.0, $text, length($text), 1);
   is(@utf8box, 8, "utf8 bbox element count");
-  my @base = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $alttext, length($alttext), 0);
+  my @base = $fnum->bbox(50.0, $alttext, length($alttext), 0);
   is(@base, 8, "alt bbox element count");
   my $maxdiff = $fontname_pfb eq $deffont ? 0 : $base[2] / 3;
   print "# (@utf8box vs @base)\n";
@@ -88,9 +87,9 @@ SKIP:
       "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)");
 
   # hand-encoded UTF8 drawing
-  ok(Imager::Font::T1::i_t1_text($backgr, 10, 140, $bgcolor, $fnum, 32, $text, length($text), 1,1), "draw hand-encoded UTF8");
+  ok($fnum->text($backgr, 10, 140, $bgcolor, 32, $text, length($text), 1,1), "draw hand-encoded UTF8");
 
-  ok(Imager::Font::T1::i_t1_cp($backgr, 80, 140, 1, $fnum, 32, $text, length($text), 1, 1), 
+  ok($fnum->cp($backgr, 80, 140, 1, 32, $text, length($text), 1, 1), 
       "cp hand-encoded UTF8");
 
   # ok, try native perl UTF8 if available
@@ -102,16 +101,16 @@ SKIP:
     # versions
     eval q{$text = "A\xA1\x{2010}A"}; # A, a with ogonek, HYPHEN, A in our test font
     #$text = "A".chr(0xA1).chr(0x2010)."A"; # this one works too
-    ok(Imager::Font::T1::i_t1_text($backgr, 10, 180, $bgcolor, $fnum, 32, $text, length($text), 1),
+    ok($fnum->text($backgr, 10, 180, $bgcolor, 32, $text, length($text), 1),
         "draw UTF8");
-    ok(Imager::Font::T1::i_t1_cp($backgr, 80, 180, 1, $fnum, 32, $text, length($text), 1),
+    ok($fnum->cp($backgr, 80, 180, 1, 32, $text, length($text), 1),
         "cp UTF8");
-    @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 0);
+    @utf8box = $fnum->bbox(50.0, $text, length($text), 0);
     is(@utf8box, 8, "native utf8 bbox element count");
     ok(abs($utf8box[2] - $base[2]) <= $maxdiff, 
       "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)");
     eval q{$text = "A\xA1\xA2\x01\x1F\x{0100}A"};
-    ok(Imager::Font::T1::i_t1_text($backgr, 10, 220, $bgcolor, $fnum, 32, $text, 0, 1, 0, "uso"),
+    ok($fnum->text($backgr, 10, 220, $bgcolor, 32, $text, 0, 1, 0, "uso"),
        "more complex output");
   }
 
@@ -121,13 +120,7 @@ SKIP:
   i_writeppm_wiol($backgr, $IO);
   close(FH);
 
-  my $rc=Imager::Font::T1::i_t1_destroy($fnum);
-  unless (ok($rc >= 0, "i_t1_destroy")) {
-    print "# i_t1_destroy failed: rc=$rc\n";
-  }
-
-  print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"eses",4) ),"\n";
-  print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"llll",4) ),"\n";
+  undef $fnum;
 
   # character existance tests - uses the special ExistenceTest font
   my $exists_font = 'fontfiles/ExistenceTest.pfb';
@@ -135,22 +128,22 @@ SKIP:
   
   -e $exists_font or die "$exists_font not found";
     
-  my $font_num = Imager::Font::T1::i_t1_new($exists_font, $exists_afm);
+  my $font_num = Imager::Font::T1xs->new($exists_font, $exists_afm);
   SKIP: {
     ok($font_num >= 0, 'loading test font')
       or skip('Could not load test font', 6);
     # first the list interface
-    my @exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A");
+    my @exists = $font_num->has_chars("!A");
     is(@exists, 2, "return count from has_chars");
     ok($exists[0], "we have an exclamation mark");
     ok(!$exists[1], "we have no uppercase A");
 
     # then the scalar interface
-    my $exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A");
+    my $exists = $font_num->has_chars("!A");
     is(length($exists), 2, "return scalar length");
     ok(ord(substr($exists, 0, 1)), "we have an exclamation mark");
     ok(!ord(substr($exists, 1, 1)), "we have no upper-case A");
-    Imager::Font::T1::i_t1_destroy($font_num);
+    undef $font_num;
   }
   
   my $font = Imager::Font->new(file=>$exists_font, type=>'t1');
@@ -175,7 +168,7 @@ SKIP:
     isnt($bbox[2], $bbox[5], "different advance to pos_width");
 
     # names
-    my $face_name = Imager::Font::T1::i_t1_face_name($font->{id});
+    my $face_name = $font->{t1font}->face_name();
     print "# face $face_name\n";
     is($face_name, 'ExistenceTest', "face name");
     $face_name = $font->face_name;
@@ -368,6 +361,40 @@ SKIP:
     ok($font, "found font by drive relative path")
       or print "# path $drive_path\n";
   }
+
+  {
+    Imager->log("Testing aa levels", 1);
+    my $f1 = Imager::Font->new(file => $deffont, type => "t1");
+    is($f1->{t1aa}, 2, "should have default aa level");
+    my $imbase = Imager->new(xsize => 100, ysize => 20);
+    ok($imbase->string(text => "test", size => 18, x => 5, y => 18,
+                      color => "#FFF", font => $f1, aa => 1),
+       "draw text with def aa level");
+    ok(Imager::Font::T1->set_aa_level(1), "set aa level to 1");
+    my $f2 = Imager::Font->new(file => $deffont, type => "t1");
+    is($f2->{t1aa}, 1, "new font has new aa level");
+    my $imaa1 = Imager->new(xsize => 100, ysize => 20);
+    ok($imaa1->string(text => "test", size => 18, x => 5, y => 18,
+                      color => "#FFF", font => $f2, aa => 1),
+       "draw text with non-def aa level");
+    isnt_image($imbase, $imaa1, "images should differ");
+    ok($f2->set_aa_level(2), "set aa level of font");
+    is($f2->{t1aa}, 2, "check new aa level");
+    my $imaa2 = Imager->new(xsize => 100, ysize => 20);
+    ok($imaa2->string(text => "test", size => 18, x => 5, y => 18,
+                      color => "#FFF", font => $f2, aa => 1),
+       "draw text with non-def but 2 aa level");
+    is_image($imbase, $imaa2, "check images match");
+  }
+
+  { # error handling check
+    my $im = Imager->new(xsize => 100, ysize => 20);
+    my $fnum = Imager::Font->new(file => $deffont, type => "t1");
+    ok(!$im->string(font => $fnum, string => "text", size => -10),
+       "set invalid size");
+    is($im->errstr, "i_t1_text(): T1_AASetString failed: Invalid Argument in Function Call",
+       "check error message");
+  }
 }
 
 #malloc_state();
diff --git a/T1/t/t30thread.t b/T1/t/t30thread.t
new file mode 100644 (file)
index 0000000..b0277ad
--- /dev/null
@@ -0,0 +1,59 @@
+#!perl -w
+use strict;
+use Imager;
+
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+# https://rt.cpan.org/Ticket/Display.html?id=65812
+# https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100
+$Test::More::VERSION =~ /^2\.00_/
+  and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*";
+
+plan tests => 8;
+
+Imager->open_log(log => "testout/t30thread.log");
+
+my $ft1 = Imager::Font->new(file => "fontfiles/dcr10.pfb", type => "t1");
+ok($ft1, "make a font");
+ok($ft1->_valid, "and it's valid");
+my $ft2;
+
+my $thr = threads->create
+  (
+   sub {
+     ok(!$ft1->_valid, "first font no longer valid");
+     $ft2 = Imager::Font->new(file => "fontfiles/dcr10.pfb", type => "t1");
+     ok($ft2, "make a new font in thread");
+     ok($ft2->_valid, "and it's valid");
+     1;
+   },
+  );
+
+ok($thr->join, "join the thread");
+ok($ft1->_valid, "original font still valid in main thread");
+is($ft2, undef, "font created in thread shouldn't be set in main thread");
+
+Imager->close_log();
diff --git a/T1/typemap b/T1/typemap
new file mode 100644 (file)
index 0000000..95e197e
--- /dev/null
@@ -0,0 +1 @@
+Imager::Font::T1xs     T_PTROBJ
index 4c95a92..680e457 100644 (file)
@@ -4,7 +4,7 @@ use Imager;
 use vars qw($VERSION @ISA);
 
 BEGIN {
-  $VERSION = "0.84";
+  $VERSION = "0.85";
 
   require XSLoader;
   XSLoader::load('Imager::File::TIFF', $VERSION);
index 3765d4f..c57486c 100644 (file)
@@ -143,3 +143,4 @@ i_tiff_has_compression(name)
 
 BOOT:
        PERL_INITIALIZE_IMAGER_CALLBACKS;
+       i_tiff_init();
\ No newline at end of file
index 1f5aa06..d94f686 100644 (file)
@@ -266,6 +266,13 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) {
 
 #endif
 
+static i_mutex_t mutex;
+
+void
+i_tiff_init(void) {
+  mutex = i_mutex_new();
+}
+
 static int save_tiff_tags(TIFF *tif, i_img *im);
 
 static void 
@@ -621,6 +628,8 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
   int current_page;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
 #ifdef USE_EXT_WARN_HANDLER
@@ -658,6 +667,7 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
     TIFFSetWarningHandlerExt(old_ext_warn_handler);
 #endif
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return NULL;
   }
 
@@ -672,6 +682,7 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
 #endif
       TIFFClose(tif);
       tiffio_context_final(&ctx);
+      i_mutex_unlock(mutex);
       return NULL;
     }
   }
@@ -686,6 +697,7 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
 #endif
   TIFFClose(tif);
   tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
 
   return im;
 }
@@ -709,6 +721,8 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
   int result_alloc = 0;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
 #ifdef USE_EXT_WARN_HANDLER
@@ -747,6 +761,7 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
     TIFFSetWarningHandlerExt(old_ext_warn_handler);
 #endif
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return NULL;
   }
 
@@ -781,6 +796,7 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
 #endif
   TIFFClose(tif);
   tiffio_context_final(&ctx);
+  i_mutex_unlock(mutex);
 
   return results;
 }
@@ -1446,6 +1462,8 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   int i;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
@@ -1472,6 +1490,7 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1480,6 +1499,7 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
       tiffio_context_final(&ctx);
+      i_mutex_unlock(mutex);
       return 0;
     }
 
@@ -1488,6 +1508,7 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
       tiffio_context_final(&ctx);
+      i_mutex_unlock(mutex);
       return 0;
     }
   }
@@ -1496,6 +1517,8 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   (void) TIFFClose(tif);
   tiffio_context_final(&ctx);
 
+  i_mutex_unlock(mutex);
+
   if (i_io_close(ig))
     return 0;
 
@@ -1522,6 +1545,8 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
   TIFFErrorHandler old_handler;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
@@ -1548,6 +1573,7 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1556,6 +1582,7 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
       tiffio_context_final(&ctx);
+      i_mutex_unlock(mutex);
       return 0;
     }
 
@@ -1564,6 +1591,7 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
       tiffio_context_final(&ctx);
+      i_mutex_unlock(mutex);
       return 0;
     }
   }
@@ -1572,6 +1600,8 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
   TIFFSetErrorHandler(old_handler);
   tiffio_context_final(&ctx);
 
+  i_mutex_unlock(mutex);
+
   if (i_io_close(ig))
     return 0;
 
@@ -1594,6 +1624,8 @@ i_writetiff_wiol(i_img *img, io_glue *ig) {
   TIFFErrorHandler old_handler;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
@@ -1619,6 +1651,7 @@ i_writetiff_wiol(i_img *img, io_glue *ig) {
     i_push_error(0, "Could not create TIFF object");
     tiffio_context_final(&ctx);
     TIFFSetErrorHandler(old_handler);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1626,12 +1659,14 @@ i_writetiff_wiol(i_img *img, io_glue *ig) {
     TIFFClose(tif);
     tiffio_context_final(&ctx);
     TIFFSetErrorHandler(old_handler);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
-    tiffio_context_final(&ctx);
+  tiffio_context_final(&ctx);
+  i_mutex_unlock(mutex);
 
   if (i_io_close(ig))
     return 0;
@@ -1662,6 +1697,8 @@ i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
   TIFFErrorHandler old_handler;
   tiffio_context_t ctx;
 
+  i_mutex_lock(mutex);
+
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
@@ -1687,6 +1724,7 @@ i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1694,12 +1732,14 @@ i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
     TIFFClose(tif);
     TIFFSetErrorHandler(old_handler);
     tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
   tiffio_context_final(&ctx);
+  i_mutex_unlock(mutex);
 
   if (i_io_close(ig))
     return 0;
index 84222b6..2e7cfe4 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "imdatatypes.h"
 
+void i_tiff_init(void);
 i_img   * i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page);
 i_img  ** i_readtiff_multi_wiol(io_glue *ig, int *count);
 undef_int i_writetiff_wiol(i_img *im, io_glue *ig);
index d08cf1f..1a6ed07 100644 (file)
@@ -178,7 +178,7 @@ Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO
 
-Imager, Imager::ExtUtils, Imager::Inline
+Imager, Imager::API, Imager::ExtUtils, Imager::Inline
 
 =cut
 EOS
@@ -187,13 +187,13 @@ close OUT;
 
 
 sub make_func_list {
-  my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log i_img_color_channels i_img_has_alpha i_img_dim i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits i_psamp i_psampf);
+  my @funcs = qw(i_img i_color i_fcolor i_fill_t mm_log mm_log i_img_color_channels i_img_has_alpha i_img_dim i_DF i_DFc i_DFp i_DFcp i_psamp_bits i_gsamp_bits i_psamp i_psampf);
   open FUNCS, "< imexttypes.h"
     or die "Cannot open imexttypes.h: $!\n";
   my $in_struct;
   while (<FUNCS>) {
     /^typedef struct/ && ++$in_struct;
-    if ($in_struct && /\(\*f_(io?_\w+)/) {
+    if ($in_struct && !/SKIP/ && /\(\*f_(i[om]?_\w+)/) {
       my $name = $1;
       $name =~ s/_imp$//;
       push @funcs, $name;
diff --git a/bench/.gitignore b/bench/.gitignore
new file mode 100644 (file)
index 0000000..595be54
--- /dev/null
@@ -0,0 +1,2 @@
+/largish.tif
+/largish.gif
diff --git a/bench/gifthread.pl b/bench/gifthread.pl
new file mode 100644 (file)
index 0000000..e22a242
--- /dev/null
@@ -0,0 +1,55 @@
+#!perl -w
+use strict;
+use threads;
+use Imager;
+
+++$|;
+Imager->preload;
+
+# as a TIFF this file is large, build it from largeish.jpg if it
+# doesn't exist
+unless (-f "bench/largish.gif") {
+  my $im = Imager->new(file => "bench/largish.jpg")
+    or die "Cannot read bench/largish.jpg:", Imager->errstr;
+  $im->write(file => "bench/largish.gif")
+    or die "Cannot write bench/largish.gif:", $im->errstr;
+}
+
+my @tests =
+  (
+   [ "bench/largish.gif", "" ],
+   [ "GIF/testimg/scale.gif", "" ],
+   [ "GIF/testimg/nocmap.gif", "Image does not have a local or a global color map" ],
+  );
+
+my @threads;
+my $name = "A";
+for my $test (@tests) {
+  push @threads,
+    threads->create
+      (
+       sub  {
+        my ($file, $result, $name) = @_;
+        for (1 .. 100000) {
+          print $name;
+          my $im = Imager->new(file => $file);
+          if ($result) {
+            $im and die "Expected error from $file, got image";
+            Imager->errstr eq $result
+              or die "Expected error '$result', got '",Imager->errstr, "'"
+          }
+          else {
+            $im or die "Expected image got error '", Imager->errstr, "'";
+          }
+        }
+        return;
+       },
+       @$test,
+       $name
+      );
+  ++$name;
+}
+
+for my $t (@threads) {
+  $t->join();
+}
diff --git a/bench/largish.jpg b/bench/largish.jpg
new file mode 100644 (file)
index 0000000..de91962
Binary files /dev/null and b/bench/largish.jpg differ
diff --git a/bench/tifthread.pl b/bench/tifthread.pl
new file mode 100644 (file)
index 0000000..0a66ce5
--- /dev/null
@@ -0,0 +1,55 @@
+#!perl -w
+use strict;
+use threads;
+use Imager;
+
+++$|;
+Imager->preload;
+
+# as a TIFF this file is large, build it from largeish.jpg if it
+# doesn't exist
+unless (-f "bench/largish.tif") {
+  my $im = Imager->new(file => "bench/largish.jpg")
+    or die "Cannot read bench/largish.jpg:", Imager->errstr;
+  $im->write(file => "bench/largish.tif")
+    or die "Cannot write bench.largish.tif:", $im->errstr;
+}
+
+my @tests =
+  (
+   [ "bench/largish.tif", "" ],
+   [ "TIFF/testimg/grey16.tif", "" ],
+   [ "TIFF/testimg/comp4bad.tif", "(Iolayer): Read error at scanline 120; got 0 bytes, expected 32" ],
+  );
+
+my @threads;
+my $name = "A";
+for my $test (@tests) {
+  push @threads,
+    threads->create
+      (
+       sub  {
+        my ($file, $result, $name) = @_;
+        for (1 .. 100000) {
+          print $name;
+          my $im = Imager->new(file => $file);
+          if ($result) {
+            $im and die "Expected error from $file, got image";
+            Imager->errstr eq $result
+              or die "Expected error '$result', got '",Imager->errstr, "'"
+          }
+          else {
+            $im or die "Expected image got error '", Imager->errstr, "'";
+          }
+        }
+        return;
+       },
+       @$test,
+       $name
+      );
+  ++$name;
+}
+
+for my $t (@threads) {
+  $t->join();
+}
diff --git a/bmp.c b/bmp.c
index 4f770b8..0e8fe8e 100644 (file)
--- a/bmp.c
+++ b/bmp.c
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include <stdarg.h>
 #include "imageri.h"
 
@@ -76,6 +77,7 @@ Never compresses the image.
 */
 int
 i_writebmp_wiol(i_img *im, io_glue *ig) {
+  dIMCTXim(im);
   i_clear_error();
 
   /* pick a format */
@@ -116,8 +118,9 @@ i_readbmp_wiol(io_glue *ig, int allow_incomplete) {
   i_packed_t xsize, ysize, planes, bit_count, compression, size_image, xres, yres;
   i_packed_t clr_used, clr_important, offbits;
   i_img *im;
+  dIMCTXio(ig);
 
-  mm_log((1, "i_readbmp_wiol(ig %p)\n", ig));
+  im_log((aIMCTX, 1, "i_readbmp_wiol(ig %p)\n", ig));
   
   i_clear_error();
 
@@ -135,7 +138,7 @@ i_readbmp_wiol(io_glue *ig, int allow_incomplete) {
     return 0;
   }
 
-  mm_log((1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d "
+  im_log((aIMCTX, 1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d "
           "bit_count %d compression %d size %d xres %d yres %d clr_used %d "
           "clr_important %d\n", (int)filesize, (int)offbits, (int)xsize,
          (int)ysize, (int)planes, (int)bit_count, (int)compression, 
@@ -143,7 +146,7 @@ i_readbmp_wiol(io_glue *ig, int allow_incomplete) {
           (int)clr_important));
 
   if (!i_int_check_image_file_limits(xsize, abs(ysize), 3, sizeof(i_sample_t))) {
-    mm_log((1, "i_readbmp_wiol: image size exceeds limits\n"));
+    im_log((aIMCTX, 1, "i_readbmp_wiol: image size exceeds limits\n"));
     return NULL;
   }
   
@@ -171,7 +174,7 @@ i_readbmp_wiol(io_glue *ig, int allow_incomplete) {
     break;
 
   default:
-    i_push_errorf(0, "unknown bit count for BMP file (%d)", (int)bit_count);
+    im_push_errorf(aIMCTX, 0, "unknown bit count for BMP file (%d)", (int)bit_count);
     return NULL;
   }
 
@@ -273,7 +276,10 @@ read_packed(io_glue *ig, char *format, ...) {
       break;
       
     default:
-      i_fatal(1, "Unknown read_packed format code 0x%02x", code);
+      {
+       dIMCTXio(ig);
+       im_fatal(aIMCTX, 1, "Unknown read_packed format code 0x%02x", code);
+      }
     }
   }
   return 1;
@@ -325,7 +331,10 @@ write_packed(io_glue *ig, char *format, ...) {
       break;
 
     default:
-      i_fatal(1, "Unknown write_packed format code 0x%02x", *format);
+      {
+       dIMCTXio(ig);
+       im_fatal(aIMCTX, 1, "Unknown write_packed format code 0x%02x", *format);
+      }
     }
     ++format;
   }
@@ -350,6 +359,7 @@ int write_bmphead(io_glue *ig, i_img *im, int bit_count, int data_size) {
   int got_xres, got_yres, aspect_only;
   int colors_used = 0;
   int offset = FILEHEAD_SIZE + INFOHEAD_SIZE;
+  dIMCTXim(im);
 
   if (im->xsize > SIGNMAX32 || im->ysize > SIGNMAX32) {
     i_push_error(0, "image too large to write to BMP");
@@ -451,6 +461,7 @@ write_1bit_data(io_glue *ig, i_img *im) {
   int line_size = (im->xsize+7) / 8;
   int x, y;
   int unpacked_size;
+  dIMCTXim(im);
 
   /* round up to nearest multiple of four */
   line_size = (line_size + 3) / 4 * 4;
@@ -522,6 +533,7 @@ write_4bit_data(io_glue *ig, i_img *im) {
   int line_size = (im->xsize+1) / 2;
   int x, y;
   int unpacked_size;
+  dIMCTXim(im);
 
   /* round up to nearest multiple of four */
   line_size = (line_size + 3) / 4 * 4;
@@ -580,6 +592,7 @@ write_8bit_data(io_glue *ig, i_img *im) {
   int line_size = im->xsize;
   int y;
   int unpacked_size;
+  dIMCTXim(im);
 
   /* round up to nearest multiple of four */
   line_size = (line_size + 3) / 4 * 4;
@@ -627,6 +640,7 @@ write_24bit_data(io_glue *ig, i_img *im) {
   int y;
   int line_size = 3 * im->xsize;
   i_color bg;
+  dIMCTXim(im);
 
   i_get_file_background(im, &bg);
 
@@ -681,6 +695,7 @@ read_bmp_pal(io_glue *ig, i_img *im, int count) {
   int i;
   i_packed_t r, g, b, x;
   i_color c;
+  dIMCTXio(ig);
   
   for (i = 0; i < count; ++i) {
     if (!read_packed(ig, "CCCC", &b, &g, &r, &x)) {
@@ -719,9 +734,10 @@ read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   int bit;
   unsigned char *in;
   long base_offset;
+  dIMCTXio(ig);
 
   if (compression != BI_RGB) {
-    i_push_errorf(0, "unknown 1-bit BMP compression (%d)", compression);
+    im_push_errorf(aIMCTX, 0, "unknown 1-bit BMP compression (%d)", compression);
     return NULL;
   }
 
@@ -752,13 +768,13 @@ read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   if (!clr_used)
     clr_used = 2;
   if (clr_used < 0 || clr_used > 2) {
-    i_push_errorf(0, "out of range colors used (%d)", clr_used);
+    im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used);
     return NULL;
   }
 
   base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4;
   if (offbits < base_offset) {
-    i_push_errorf(0, "image data offset too small (%ld)", offbits);
+    im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits);
     return NULL;
   }
 
@@ -847,6 +863,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   int size, i;
   long base_offset;
   int starty;
+  dIMCTXio(ig);
 
   /* line_size is going to be smaller than xsize in most cases (and
      when it's not, xsize is itself small), and hence not overflow */
@@ -869,13 +886,13 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
     clr_used = 16;
 
   if (clr_used > 16 || clr_used < 0) {
-    i_push_errorf(0, "out of range colors used (%d)", clr_used);
+    im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used);
     return NULL;
   }
 
   base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4;
   if (offbits < base_offset) {
-    i_push_errorf(0, "image data offset too small (%ld)", offbits);
+    im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits);
     return NULL;
   }
 
@@ -967,7 +984,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
          myfree(packed);
          myfree(line);
          i_push_error(0, "invalid data during decompression");
-         mm_log((1, "read 4-bit: scanline overflow x %d + count %d vs xlimit %d (y %d)\n",
+         im_log((aIMCTX, 1, "read 4-bit: scanline overflow x %d + count %d vs xlimit %d (y %d)\n",
                  (int)x, count, (int)xlimit, (int)y));
          i_img_destroy(im);
          return NULL;
@@ -1017,7 +1034,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
            myfree(packed);
            myfree(line);
            i_push_error(0, "invalid data during decompression");
-           mm_log((1, "read 4-bit: scanline overflow (unpacked) x %d + count %d vs xlimit %d (y %d)\n",
+           im_log((aIMCTX, 1, "read 4-bit: scanline overflow (unpacked) x %d + count %d vs xlimit %d (y %d)\n",
                  (int)x, count, (int)xlimit, (int)y));
            i_img_destroy(im);
            return NULL;
@@ -1052,7 +1069,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   else { /*if (compression == BI_RLE4) {*/
     myfree(packed);
     myfree(line);
-    i_push_errorf(0, "unknown 4-bit BMP compression (%d)", compression);
+    im_push_errorf(aIMCTX, 0, "unknown 4-bit BMP compression (%d)", compression);
     i_img_destroy(im);
     return NULL;
   }
@@ -1077,6 +1094,7 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   i_palidx *line;
   int line_size = xsize;
   long base_offset;
+  dIMCTXio(ig);
 
   line_size = (line_size+3) / 4 * 4;
   if (line_size < xsize) { /* if it overflowed (unlikely, but check) */
@@ -1100,13 +1118,13 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   if (!clr_used)
     clr_used = 256;
   if (clr_used > 256 || clr_used < 0) {
-    i_push_errorf(0, "out of range colors used (%d)", clr_used);
+    im_push_errorf(aIMCTX, 0, "out of range colors used (%d)", clr_used);
     return NULL;
   }
 
   base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4;
   if (offbits < base_offset) {
-    i_push_errorf(0, "image data offset too small (%ld)", offbits);
+    im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits);
     return NULL;
   }
 
@@ -1249,7 +1267,7 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used,
   }
   else { 
     myfree(line);
-    i_push_errorf(0, "unknown 8-bit BMP compression (%d)", compression);
+    im_push_errorf(aIMCTX, 0, "unknown 8-bit BMP compression (%d)", compression);
     i_img_destroy(im);
     return NULL;
   }
@@ -1322,6 +1340,7 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
   const char *compression_name;
   int bytes;
   long base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE;
+  dIMCTXio(ig);
   
   unpack_code[0] = *("v3V"+pix_size-2);
   unpack_code[1] = '\0';
@@ -1368,7 +1387,7 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
         return 0;
       }
       if (rmask == 0) {
-       i_push_errorf(0, "Zero mask for channel %d", i);
+       im_push_errorf(aIMCTX, 0, "Zero mask for channel %d", i);
        return NULL;
       }
       masks.masks[i] = rmask;
@@ -1392,12 +1411,12 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count,
     base_offset += 3 * 4;
   }
   else {
-    i_push_errorf(0, "unknown 24-bit BMP compression (%d)", compression);
+    im_push_errorf(aIMCTX, 0, "unknown 24-bit BMP compression (%d)", compression);
     return NULL;
   }
 
   if (offbits < base_offset) {
-    i_push_errorf(0, "image data offset too small (%ld)", offbits);
+    im_push_errorf(aIMCTX, 0, "image data offset too small (%ld)", offbits);
     return NULL;
   }
 
diff --git a/context.c b/context.c
new file mode 100644 (file)
index 0000000..0598a6a
--- /dev/null
+++ b/context.c
@@ -0,0 +1,289 @@
+#include "imageri.h"
+#include <stdio.h>
+
+static volatile im_slot_t slot_count = 1;
+static im_slot_destroy_t *volatile slot_destructors;
+static volatile i_mutex_t slot_mutex;
+
+/*
+=item im_context_new()
+
+Create a new Imager context object.
+
+=cut
+*/
+
+im_context_t
+im_context_new(void) {
+  im_context_t ctx = malloc(sizeof(im_context_struct));
+  int i;
+
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
+  if (!ctx)
+    return NULL;
+  
+  ctx->error_sp = IM_ERROR_COUNT-1;
+  for (i = 0; i < IM_ERROR_COUNT; ++i) {
+    ctx->error_alloc[i] = 0;
+    ctx->error_stack[i].msg = NULL;
+    ctx->error_stack[i].code = 0;
+  }
+#ifdef IMAGER_LOG
+  ctx->log_level = 0;
+  ctx->lg_file = NULL;
+#endif
+  ctx->max_width = 0;
+  ctx->max_height = 0;
+  ctx->max_bytes = DEF_BYTES_LIMIT;
+
+  ctx->slot_alloc = slot_count;
+  ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
+  if (!ctx->slots) {
+    free(ctx);
+    return NULL;
+  }
+
+  ctx->refcount = 1;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context: created %p\n", ctx);
+#endif
+
+
+  return ctx;
+}
+
+/*
+=item im_context_refinc(ctx, where)
+X<im_context_refinc API>
+=section Context objects
+=synopsis im_context_refinc(aIMCTX, "a description");
+
+Add a new reference to the context.
+
+=cut
+*/
+
+void
+im_context_refinc(im_context_t ctx, const char *where) {
+  ++ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
+         ctx, (unsigned long)ctx->refcount);
+#endif
+}
+
+/*
+=item im_context_refdec(ctx, where)
+X<im_context_refdec API>
+=section Context objects
+=synopsis im_context_refdec(aIMCTX, "a description");
+
+Remove a reference to the context, releasing it if all references have
+been removed.
+
+=cut
+*/
+
+void
+im_context_refdec(im_context_t ctx, const char *where) {
+  int i;
+  im_slot_t slot;
+
+  im_assert(ctx->refcount > 0);
+
+  --ctx->refcount;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
+         ctx, (unsigned long)ctx->refcount);
+#endif
+
+  if (ctx->refcount != 0)
+    return;
+
+  /* lock here to avoid slot_destructors from being moved under us */
+  i_mutex_lock(slot_mutex);
+  for (slot = 0; slot < ctx->slot_alloc; ++slot) {
+    if (ctx->slots[slot] && slot_destructors[slot])
+      slot_destructors[slot](ctx->slots[slot]);
+  }
+  i_mutex_unlock(slot_mutex);
+
+  free(ctx->slots);
+
+  for (i = 0; i < IM_ERROR_COUNT; ++i) {
+    if (ctx->error_stack[i].msg)
+      myfree(ctx->error_stack[i].msg);
+  }
+#ifdef IMAGER_LOG
+  if (ctx->lg_file && ctx->own_log)
+    fclose(ctx->lg_file);
+#endif
+
+  free(ctx);
+}
+
+/*
+=item im_context_clone(ctx)
+
+Clone an Imager context object, returning the result.
+
+The error stack is not copied from the original context.
+
+=cut
+*/
+
+im_context_t
+im_context_clone(im_context_t ctx, const char *where) {
+  im_context_t nctx = malloc(sizeof(im_context_struct));
+  int i;
+
+  if (!nctx)
+    return NULL;
+
+  nctx->slot_alloc = slot_count;
+  nctx->slots = calloc(sizeof(void *), nctx->slot_alloc);
+  if (!nctx->slots) {
+    free(nctx);
+    return NULL;
+  }
+
+  nctx->error_sp = IM_ERROR_COUNT-1;
+  for (i = 0; i < IM_ERROR_COUNT; ++i) {
+    nctx->error_alloc[i] = 0;
+    nctx->error_stack[i].msg = NULL;
+  }
+#ifdef IMAGER_LOG
+  nctx->log_level = ctx->log_level;
+  if (ctx->lg_file) {
+    if (ctx->own_log) {
+      int newfd = dup(fileno(ctx->lg_file));
+      nctx->own_log = 1;
+      nctx->lg_file = fdopen(newfd, "w");
+      if (nctx->lg_file)
+       setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ);
+    }
+    else {
+      /* stderr */
+      nctx->lg_file = ctx->lg_file;
+      nctx->own_log = 0;
+    }
+  }
+  else {
+    nctx->lg_file = NULL;
+  }
+#endif
+  nctx->max_width = ctx->max_width;
+  nctx->max_height = ctx->max_height;
+  nctx->max_bytes = ctx->max_bytes;
+
+  nctx->refcount = 1;
+
+#ifdef IMAGER_TRACE_CONTEXT
+  fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
+#endif
+
+  return nctx;
+}
+
+/*
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+C<desctructor> will be called when the context is destroyed if the
+corresponding slot is non-NULL.
+
+=cut
+*/
+
+im_slot_t
+im_context_slot_new(im_slot_destroy_t destructor) {
+  im_slot_t new_slot;
+  im_slot_destroy_t *new_destructors;
+  if (!slot_mutex)
+    slot_mutex = i_mutex_new();
+
+  i_mutex_lock(slot_mutex);
+
+  new_slot = slot_count++;
+  new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
+  if (!new_destructors)
+    i_fatal(1, "Cannot allocate memory for slot destructors");
+  slot_destructors = new_destructors;
+
+  slot_destructors[new_slot] = destructor;
+
+  i_mutex_unlock(slot_mutex);
+
+  return new_slot;
+}
+
+/*
+=item im_context_slot_set(slot, value)
+
+Set the value of a slot.
+
+Returns true on success.
+
+Aborts if the slot supplied is invalid.
+
+If reallocation of slot storage fails, returns false.
+
+=cut
+*/
+
+int
+im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc) {
+    ssize_t i;
+    size_t new_alloc = slot_count;
+    void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
+
+    if (!new_slots)
+      return 0;
+
+    for (i = ctx->slot_alloc; i < new_alloc; ++i)
+      new_slots[i] = NULL;
+
+    ctx->slots = new_slots;
+    ctx->slot_alloc = new_alloc;
+  }
+
+  ctx->slots[slot] = value;
+
+  return 1;
+}
+
+/*
+=item im_context_slot_get(ctx, slot)
+
+Retrieve the value previously stored in the given slot of the context
+object.
+
+=cut
+*/
+
+void *
+im_context_slot_get(im_context_t ctx, im_slot_t slot) {
+  if (slot < 0 || slot >= slot_count) {
+    fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
+           (int)slot, (int)slot_count-1);
+    abort();
+  }
+
+  if (slot >= ctx->slot_alloc)
+    return NULL;
+
+  return ctx->slots[slot];
+}
diff --git a/conv.im b/conv.im
index f02db85..ced813e 100644 (file)
--- a/conv.im
+++ b/conv.im
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include "imageri.h"
 
@@ -19,12 +20,13 @@ i_conv(i_img *im, const double *coeff,int len) {
   double pc;
   double res[MAXCHANNELS];
   i_img *timg;
+  dIMCTXim(im);
 
-  mm_log((1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len));
-  i_clear_error();
+  im_log((aIMCTX,1,"i_conv(im %p, coeff %p, len %d)\n",im,coeff,len));
+  im_clear_error(aIMCTX);
 
   if (len < 1) {
-    i_push_error(0, "there must be at least one coefficient");
+    im_push_error(aIMCTX, 0, "there must be at least one coefficient");
     return 0;
   }
  
index 3ab6d17..2d5a8a9 100644 (file)
@@ -17,6 +17,7 @@ converting from RGBA to greyscale and back.
 =cut
 */
 
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 
 struct chan_copy {
@@ -69,17 +70,18 @@ i_convert(i_img *src, const double *coeff, int outchan, int inchan) {
   int i, j;
   int ilimit;
   i_img *im = NULL;
+  dIMCTXim(src);
 
-  mm_log((1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n",
+  im_log((aIMCTX,1,"i_convert(im %p, src %p, coeff %p,outchan %d, inchan %d)\n",
          im, src, coeff, outchan, inchan));
  
-  i_clear_error();
+  im_clear_error(aIMCTX);
 
   ilimit = inchan;
   if (ilimit > src->channels)
     ilimit = src->channels;
   if (outchan > MAXCHANNELS) {
-    i_push_error(0, "cannot have outchan > MAXCHANNELS");
+    im_push_error(aIMCTX, 0, "cannot have outchan > MAXCHANNELS");
     return 0;
   }
 
@@ -130,8 +132,8 @@ i_convert(i_img *src, const double *coeff, int outchan, int inchan) {
     i_color *colors;
     i_palidx *vals;
 
-    im = i_img_pal_new(src->xsize, src->ysize, outchan, 
-                      i_maxcolors(src));
+    im = im_img_pal_new(aIMCTX, src->xsize, src->ysize, outchan, 
+                       i_maxcolors(src));
 
     /* just translate the color table */
     count = i_colorcount(src);
index e8ef231..c660410 100644 (file)
@@ -1,6 +1,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 
 /*
@@ -129,8 +130,9 @@ llist_push(struct llist *l,const void *data) {
     }
   }
   /*   fprintf(stderr,"0x%08X\n",l->t); */
-  if (llist_llink_push(l,l->t,data)) { 
-    i_fatal(3, "out of memory\n");
+  if (llist_llink_push(l,l->t,data)) {
+    dIMCTX;
+    im_fatal(aIMCTX, 3, "out of memory\n");
   }
 }
 
diff --git a/draw.c b/draw.c
index f36949a..b74fe2e 100644 (file)
--- a/draw.c
+++ b/draw.c
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include "draw.h"
 #include "log.h"
@@ -236,6 +237,10 @@ of angles in degrees from d1 to d2, with the color.
 void
 i_arc(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,const i_color *val) {
   i_int_hlines hlines;
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_arc(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, col %p)",
+         im, i_DFcp(x, y), rad, d1, d2, val));
 
   i_int_init_hlines_img(&hlines, im);
 
@@ -264,6 +269,10 @@ of angles in degrees from d1 to d2, with the fill object.
 void
 i_arc_cfill(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,i_fill_t *fill) {
   i_int_hlines hlines;
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_arc_cfill(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, fill %p)",
+         im, i_DFcp(x, y), rad, d1, d2, fill));
 
   i_int_init_hlines_img(&hlines, im);
 
@@ -363,6 +372,10 @@ i_arc_aa(i_img *im, double x, double y, double rad, double d1, double d2,
         const i_color *val) {
   double *xvals, *yvals;
   int count;
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_arc_aa(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, col %p)",
+         im, x, y, rad, d1, d2, val));
 
   arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
 
@@ -389,6 +402,10 @@ i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
               i_fill_t *fill) {
   double *xvals, *yvals;
   int count;
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_arc_aa_cfill(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, fill %p)",
+         im, x, y, rad, d1, d2, fill));
 
   arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
 
@@ -413,12 +430,12 @@ polar_to_plane(double cx, double cy, float angle, double radius, frac *x, frac *
 
 static
 void
-make_minmax_list(i_mmarray *dot, double x, double y, double radius) {
+make_minmax_list(pIMCTX, i_mmarray *dot, double x, double y, double radius) {
   float angle = 0.0;
   float astep = radius>0.1 ? .5/radius : 10;
   frac cx, cy, lx, ly, sx, sy;
 
-  mm_log((1, "make_minmax_list(dot %p, x %.2f, y %.2f, radius %.2f)\n", dot, x, y, radius));
+  im_log((aIMCTX, 1, "make_minmax_list(dot %p, x %.2f, y %.2f, radius %.2f)\n", dot, x, y, radius));
 
   polar_to_plane(x, y, angle, radius, &sx, &sy);
   
@@ -494,12 +511,13 @@ i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
   i_mmarray dot;
   i_color temp;
   i_img_dim ly;
+  dIMCTXim(im);
 
-  mm_log((1, "i_circle_aa(im %p, centre(" i_DFp "), rad %.2f, val %p)\n",
+  im_log((aIMCTX, 1, "i_circle_aa(im %p, centre(" i_DFp "), rad %.2f, val %p)\n",
          im, i_DFcp(x, y), rad, val));
 
   i_mmarray_cr(&dot,16*im->ysize);
-  make_minmax_list(&dot, x, y, rad);
+  make_minmax_list(aIMCTX, &dot, x, y, rad);
 
   for(ly = 0; ly<im->ysize; ly++) {
     int ix, cy, minx = INT_MAX, maxx = INT_MIN;
@@ -569,11 +587,15 @@ i_circle_out(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r,
   i_img_dim x, y;
   i_img_dim dx, dy;
   int error;
+  dIMCTXim(im);
 
-  i_clear_error();
+  im_log((aIMCTX, 1, "i_circle_out(im %p, centre(" i_DFp "), rad %" i_DF ", col %p)\n",
+         im, i_DFcp(xc, yc), i_DFc(r), col));
+
+  im_clear_error(aIMCTX);
 
   if (r < 0) {
-    i_push_error(0, "circle: radius must be non-negative");
+    im_push_error(aIMCTX, 0, "circle: radius must be non-negative");
     return 0;
   }
 
@@ -684,11 +706,15 @@ i_arc_out(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r,
   i_img_dim seg2 = scale * 4;
   i_img_dim seg3 = scale * 6;
   i_img_dim seg4 = scale * 8;
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_arc_out(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)",
+         im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col));
 
-  i_clear_error();
+  im_clear_error(aIMCTX);
 
   if (r <= 0) {
-    i_push_error(0, "arc: radius must be non-negative");
+    im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
     return 0;
   }
   if (d1 + 360 <= d2)
@@ -820,10 +846,14 @@ i_circle_out_aa(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r, const i_colo
   double t;
   i_color workc = *col;
   int orig_alpha = col->channel[3];
+  dIMCTXim(im);
+
+  im_log((aIMCTX,1,"i_circle_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", col %p)",
+         im, i_DFcp(xc, yc), i_DFc(r), col));
 
-  i_clear_error();
+  im_clear_error(aIMCTX);
   if (r <= 0) {
-    i_push_error(0, "arc: radius must be non-negative");
+    im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
     return 0;
   }
   i = r;
@@ -930,10 +960,14 @@ i_arc_out_aa(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r, double d1, doub
   i_img_dim seg2 = scale * 4;
   i_img_dim seg3 = scale * 6;
   i_img_dim seg4 = scale * 8;
+  dIMCTXim(im);
 
-  i_clear_error();
+  im_log((aIMCTX,1,"i_arc_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)",
+         im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col));
+
+  im_clear_error(aIMCTX);
   if (r <= 0) {
-    i_push_error(0, "arc: radius must be non-negative");
+    im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
     return 0;
   }
   if (d1 + 360 <= d2)
@@ -1053,7 +1087,9 @@ Outlines the box from (x1,y1) to (x2,y2) inclusive with I<color>.
 void
 i_box(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val) {
   i_img_dim x,y;
-  mm_log((1,"i_box(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
+  dIMCTXim(im);
+
+  im_log((aIMCTX, 1,"i_box(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
          im, i_DFcp(x1,y1), i_DFcp(x2,y2), val));
   for(x=x1;x<x2+1;x++) {
     i_ppix(im,x,y1,val);
@@ -1080,8 +1116,9 @@ void
 i_box_filled(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_color *val) {
   i_img_dim x, y, width;
   i_palidx index;
+  dIMCTXim(im);
 
-  mm_log((1,"i_box_filled(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
+  im_log((aIMCTX,1,"i_box_filled(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
          im, i_DFcp(x1, y1), i_DFcp(x2,y2) ,val));
 
   if (x1 > x2 || y1 > y2
@@ -1140,8 +1177,9 @@ color.
 int
 i_box_filledf(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_fcolor *val) {
   i_img_dim x, y, width;
+  dIMCTXim(im);
 
-  mm_log((1,"i_box_filledf(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
+  im_log((aIMCTX, 1,"i_box_filledf(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
          im, i_DFcp(x1, y1), i_DFcp(x2, y2), val));
 
   if (x1 > x2 || y1 > y2
@@ -1198,8 +1236,9 @@ Fills the box from (x1,y1) to (x2,y2) inclusive with fill.
 void
 i_box_cfill(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_fill_t *fill) {
   i_render r;
+  dIMCTXim(im);
 
-  mm_log((1,"i_box_cfill(im* %p, p1(" i_DFp "), p2(" i_DFp "), fill %p)\n",
+  im_log((aIMCTX,1,"i_box_cfill(im* %p, p1(" i_DFp "), p2(" i_DFp "), fill %p)\n",
          im, i_DFcp(x1, y1), i_DFcp(x2,y2), fill));
 
   ++x2;
@@ -1816,11 +1855,15 @@ i_flood_fill(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *dcol) {
   struct i_bitmap *btm;
   i_img_dim x, y;
   i_color val;
+  dIMCTXim(im);
+
+  im_log((aIMCTX, 1, "i_flood_fill(im %p, seed(" i_DFp "), col %p)",
+          im, i_DFcp(seedx, seedy), dcol));
 
-  i_clear_error();
+  im_clear_error(aIMCTX);
   if (seedx < 0 || seedx >= im->xsize ||
       seedy < 0 || seedy >= im->ysize) {
-    i_push_error(0, "i_flood_cfill: Seed pixel outside of image");
+    im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
     return 0;
   }
 
@@ -1857,12 +1900,16 @@ i_flood_cfill(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill) {
   i_img_dim bxmin, bxmax, bymin, bymax;
   struct i_bitmap *btm;
   i_color val;
+  dIMCTXim(im);
 
-  i_clear_error();
+  im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), fill %p)",
+          im, i_DFcp(seedx, seedy), fill));
+
+  im_clear_error(aIMCTX);
   
   if (seedx < 0 || seedx >= im->xsize ||
       seedy < 0 || seedy >= im->ysize) {
-    i_push_error(0, "i_flood_cfill: Seed pixel outside of image");
+    im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
     return 0;
   }
 
@@ -1899,11 +1946,15 @@ i_flood_fill_border(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *
   i_img_dim bxmin, bxmax, bymin, bymax;
   struct i_bitmap *btm;
   i_img_dim x, y;
+  dIMCTXim(im);
+
+  im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), dcol %p, border %p)",
+          im, i_DFcp(seedx, seedy), dcol, border));
 
-  i_clear_error();
+  im_clear_error(aIMCTX);
   if (seedx < 0 || seedx >= im->xsize ||
       seedy < 0 || seedy >= im->ysize) {
-    i_push_error(0, "i_flood_cfill: Seed pixel outside of image");
+    im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
     return 0;
   }
 
@@ -1938,12 +1989,16 @@ i_flood_cfill_border(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill
                     const i_color *border) {
   i_img_dim bxmin, bxmax, bymin, bymax;
   struct i_bitmap *btm;
+  dIMCTXim(im);
+
+  im_log((aIMCTX, 1, "i_flood_cfill_border(im %p, seed(" i_DFp "), fill %p, border %p)",
+          im, i_DFcp(seedx, seedy), fill, border));
 
-  i_clear_error();
+  im_clear_error(aIMCTX);
   
   if (seedx < 0 || seedx >= im->xsize ||
       seedy < 0 || seedy >= im->ysize) {
-    i_push_error(0, "i_flood_cfill_border: Seed pixel outside of image");
+    im_push_error(aIMCTX, 0, "i_flood_cfill_border: Seed pixel outside of image");
     return 0;
   }
 
index 8838038..faa605d 100644 (file)
@@ -11,7 +11,7 @@ typedef HMODULE minthandle_t;
 typedef void *minthandle_t; 
 #endif 
 
-#include "plug.h"
+#include "ext.h"
 
 struct DSO_handle_tag {
   minthandle_t handle;
@@ -24,14 +24,18 @@ struct DSO_handle_tag {
 /* #include "XSUB.h"  so we can compile on threaded perls */
 #include "imageri.h"
 
+static im_context_t
+do_get_context(void) {
+  return im_get_context();
+}
+
 static symbol_table_t symbol_table=
   {
     i_has_format,
     ICL_set_internal,
     ICL_info,
-    i_img_new,
-    i_img_empty,
-    i_img_empty_ch,
+    do_get_context,
+    im_img_empty_ch,
     i_img_exorcise,
     i_img_info,
     i_img_setmask,
diff --git a/error.c b/error.c
index 84785a2..03a271c 100644 (file)
--- a/error.c
+++ b/error.c
@@ -61,112 +61,24 @@ C).  The Perl level won't use all of this.
 =cut
 */
 
-#include "imager.h"
+#include "imageri.h"
 #include <stdio.h>
 #include <stdlib.h>
 
-/* we never actually use the last item - it's the NULL terminator */
-#define ERRSTK 20
-static i_errmsg error_stack[ERRSTK];
-static int error_sp = ERRSTK - 1;
-/* we track the amount of space used each string, so we don't reallocate 
-   space unless we need to.
-   This also means that a memory tracking library may see the memory 
-   allocated for this as a leak. */
-static int error_space[ERRSTK];
-
-static i_error_cb error_cb;
-static i_failed_cb failed_cb;
-static int failures_fatal;
-static char *argv0;
-
 /*
-=item i_set_argv0(char const *program)
-
-Sets the name of the program to be displayed in fatal error messages.
-
-The simplest way to use this is just:
-
-  i_set_argv0(argv[0]);
-
-when your program starts.
-*/
-void i_set_argv0(char const *name) {
-  char *dupl;
-  if (!name)
-    return;
-  /* if the user has an existing string of MAXINT length then
-     the system is broken anyway */
-  dupl = mymalloc(strlen(name)+1); /* check 17jul05 tonyc */
-  strcpy(dupl, name);
-  if (argv0)
-    myfree(argv0);
-  argv0 = dupl;
-}
-
-/*
-=item i_set_failure_fatal(int failure_fatal)
-
-If failure_fatal is non-zero then any future failures will result in
-Imager exiting your program with a message describing the failure.
-
-Returns the previous setting.
-
-=cut
-*/
-int i_set_failures_fatal(int fatal) {
-  int old = failures_fatal;
-  failures_fatal = fatal;
-
-  return old;
-}
-
-/*
-=item i_set_error_cb(i_error_cb)
-
-Sets a callback function that is called each time an error is pushed
-onto the error stack.
-
-Returns the previous callback.
-
-i_set_failed_cb() is probably more useful.
-
-=cut
-*/
-i_error_cb i_set_error_cb(i_error_cb cb) {
-  i_error_cb old = error_cb;
-  error_cb = cb;
-
-  return old;
-}
-
-/*
-=item i_set_failed_cb(i_failed_cb cb)
-
-Sets a callback function that is called each time an Imager function
-fails.
-
-Returns the previous callback.
-
-=cut
-*/
-i_failed_cb i_set_failed_cb(i_failed_cb cb) {
-  i_failed_cb old = failed_cb;
-  failed_cb = cb;
-
-  return old;
-}
-
-/*
-=item i_errors()
+=item im_errors(ctx)
+=synopsis i_errmsg *errors = im_errors(aIMCTX);
+=synopsis i_errmsg *errors = i_errors();
 
 Returns a pointer to the first element of an array of error messages,
 terminated by a NULL pointer.  The highest level message is first.
 
+Also callable as C<i_errors()>.
+
 =cut
 */
-i_errmsg *i_errors() {
-  return error_stack + error_sp;
+i_errmsg *im_errors(im_context_t ctx) {
+  return ctx->error_stack + ctx->error_sp;
 }
 
 /*
@@ -182,7 +94,9 @@ the mark.
 
 =over
 
-=item i_clear_error()
+=item im_clear_error(ctx)
+X<im_clear_error API>X<i_clear_error API>
+=synopsis im_clear_error(aIMCTX);
 =synopsis i_clear_error();
 =category Error handling
 
@@ -190,27 +104,33 @@ Clears the error stack.
 
 Called by any Imager function before doing any other processing.
 
+Also callable as C<i_clear_error()>.
+
 =cut
 */
-void i_clear_error() {
+
+void
+im_clear_error(im_context_t ctx) {
 #ifdef IMAGER_DEBUG_MALLOC
   int i;
 
-  for (i = 0; i < ERRSTK; ++i) {
-    if (error_space[i]) {
-      myfree(error_stack[i].msg);
-      error_stack[i].msg = NULL;
-      error_space[i] = 0;
+  for (i = 0; i < IM_ERROR_COUNT; ++i) {
+    if (ctx->error_space[i]) {
+      myfree(ctx->error_stack[i].msg);
+      ctx->error_stack[i].msg = NULL;
+      ctx->error_space[i] = 0;
     }
   }
 #endif
-  error_sp = ERRSTK-1;
+  ctx->error_sp = IM_ERROR_COUNT-1;
 }
 
 /*
-=item i_push_error(int code, char const *msg)
+=item im_push_error(ctx, code, message)
+X<im_push_error API>X<i_push_error API>
 =synopsis i_push_error(0, "Yep, it's broken");
 =synopsis i_push_error(errno, "Error writing");
+=synopsis im_push_error(aIMCTX, 0, "Something is wrong");
 =category Error handling
 
 Called by an Imager function to push an error message onto the stack.
@@ -221,34 +141,35 @@ error handling is calling function that does.).
 
 =cut
 */
-void i_push_error(int code, char const *msg) {
+void
+im_push_error(im_context_t ctx, int code, char const *msg) {
   size_t size = strlen(msg)+1;
 
-  if (error_sp <= 0)
+  if (ctx->error_sp <= 0)
     /* bad, bad programmer */
     return;
 
-  --error_sp;
-  if (error_space[error_sp] < size) {
-    if (error_stack[error_sp].msg)
-      myfree(error_stack[error_sp].msg);
+  --ctx->error_sp;
+  if (ctx->error_alloc[ctx->error_sp] < size) {
+    if (ctx->error_stack[ctx->error_sp].msg)
+      myfree(ctx->error_stack[ctx->error_sp].msg);
     /* memory allocated on the following line is only ever released when 
        we need a bigger string */
     /* size is size (len+1) of an existing string, overflow would mean
        the system is broken anyway */
-    error_stack[error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
-    error_space[error_sp] = size;
+    ctx->error_stack[ctx->error_sp].msg = mymalloc(size); /* checked 17jul05 tonyc */
+    ctx->error_alloc[ctx->error_sp] = size;
   }
-  strcpy(error_stack[error_sp].msg, msg);
-  error_stack[error_sp].code = code;
-
-  if (error_cb)
-    error_cb(code, msg);
+  strcpy(ctx->error_stack[ctx->error_sp].msg, msg);
+  ctx->error_stack[ctx->error_sp].code = code;
 }
 
 /*
-=item i_push_errorvf(int C<code>, char const *C<fmt>, va_list C<ap>)
-
+=item im_push_errorvf(ctx, code, format, args)
+X<im_push_error_vf API>X<i_push_errorvf API>
+=synopsis va_args args;
+=synopsis va_start(args, lastarg);
+=synopsis im_push_errorvf(ctx, code, format, args);
 =category Error handling
 
 Intended for use by higher level functions, takes a varargs pointer
@@ -256,9 +177,12 @@ and a format to produce the finally pushed error message.
 
 Does not support perl specific format codes.
 
+Also callable as C<i_push_errorvf(code, format, args)>
+
 =cut
 */
-void i_push_errorvf(int code, char const *fmt, va_list ap) {
+void 
+im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list ap) {
   char buf[1024];
 #if defined(IMAGER_VSNPRINTF)
   vsnprintf(buf, sizeof(buf), fmt, ap);
@@ -271,7 +195,7 @@ void i_push_errorvf(int code, char const *fmt, va_list ap) {
    */
   vsprintf(buf, fmt, ap);
 #endif
-  i_push_error(code, buf);
+  im_push_error(ctx, code, buf);
 }
 
 /*
@@ -285,13 +209,33 @@ Does not support perl specific format codes.
 
 =cut
 */
-void i_push_errorf(int code, char const *fmt, ...) {
+void
+i_push_errorf(int code, char const *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   i_push_errorvf(code, fmt, ap);
   va_end(ap);
 }
 
+/*
+=item im_push_errorf(ctx, code, char const *fmt, ...)
+=synopsis im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno);
+=category Error handling
+
+A version of im_push_error() that does printf() like formatting.
+
+Does not support perl specific format codes.
+
+=cut
+*/
+void
+im_push_errorf(im_context_t ctx, int code, char const *fmt, ...) {
+  va_list ap;
+  va_start(ap, fmt);
+  im_push_errorvf(ctx, code, fmt, ap);
+  va_end(ap);
+}
+
 #ifdef IMAGER_I_FAILED
 #error "This isn't used and is untested"
 
@@ -357,11 +301,15 @@ int i_failed(int code, char const *msg) {
 
 #endif
 
+#ifdef IM_ASSERT
+
 /*
 =item im_assert_fail(file, line, message)
 
 Called when an im_assert() assertion fails.
 
+Only available when Imager is built with assertions.
+
 =cut
 */
 
@@ -369,9 +317,11 @@ void
 im_assert_fail(char const *file, int line, char const *message) {
   fprintf(stderr, "Assertion failed line %d file %s: %s\n", 
          line, file, message);
-  exit(EXIT_FAILURE);
+  abort();
 }
 
+#endif
+
 /*
 =back
 
diff --git a/ext.h b/ext.h
index dd1cfbd..9a25820 100644 (file)
--- a/ext.h
+++ b/ext.h
@@ -1,4 +1,4 @@
-#include "imager.h"
+#include "imdatatypes.h"
 
 #ifndef IMAGER_EXT_H
 #define IMAGER_EXT_H
@@ -20,4 +20,31 @@ typedef struct {
   int (*getobj)(void *hv_t,char* key,char* type,void **store);
 } UTIL_table_t;
 
+typedef struct {
+  undef_int (*i_has_format)(char *frmt);
+  i_color*(*ICL_set)(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a);
+  void (*ICL_info)(const i_color *cl);
+
+  im_context_t (*im_get_context_f)(void);
+  i_img*(*im_img_empty_ch_f)(im_context_t, i_img *im,i_img_dim x,i_img_dim y,int ch);
+  void(*i_img_exorcise_f)(i_img *im);
+
+  void(*i_img_info_f)(i_img *im,i_img_dim *info);
+  
+  void(*i_img_setmask_f)(i_img *im,int ch_mask);
+  int (*i_img_getmask_f)(i_img *im);
+  
+  /*
+  int (*i_ppix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
+  int (*i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
+  */
+  void(*i_box)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val);
+  void(*i_line)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val,int endp);
+  void(*i_arc)(i_img *im,i_img_dim x,i_img_dim y,double rad,double d1,double d2,const i_color *val);
+  void(*i_copyto)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty);
+  void(*i_copyto_trans)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans);
+  int(*i_rubthru)(i_img *im,i_img *src,i_img_dim tx,i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy);
+
+} symbol_table_t;
+
 #endif
diff --git a/fills.c b/fills.c
index 932e44f..6b46482 100644 (file)
--- a/fills.c
+++ b/fills.c
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include "imageri.h"
 
index 06ac6a1..918963e 100644 (file)
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include "imageri.h"
 #include <stdlib.h>
@@ -73,8 +74,9 @@ i_contrast(i_img *im, float intensity) {
   unsigned char ch;
   unsigned int new_color;
   i_color rcolor;
+  dIMCTXim(im);
   
-  mm_log((1,"i_contrast(im %p, intensity %f)\n", im, intensity));
+  im_log((aIMCTX, 1,"i_contrast(im %p, intensity %f)\n", im, intensity));
   
   if(intensity < 0) return;
   
@@ -100,8 +102,9 @@ s_hardinvert_low(i_img *im, int all) {
   i_img_dim x, y;
   int ch;
   int invert_channels = all ? im->channels : i_img_color_channels(im);
+  dIMCTXim(im);
 
-  mm_log((1,"i_hardinvert(im %p)\n", im));
+  im_log((aIMCTX,1,"i_hardinvert)low(im %p, all %d)\n", im, all));
   
 #code im->bits <= 8  
   IM_COLOR *row, *entry;
@@ -184,8 +187,9 @@ i_noise(i_img *im, float amount, unsigned char type) {
   float damount = amount * 2;
   i_color rcolor;
   int color_inc = 0;
+  dIMCTXim(im);
   
-  mm_log((1,"i_noise(im %p, intensity %.2f\n", im, amount));
+  im_log((aIMCTX, 1,"i_noise(im %p, intensity %.2f\n", im, amount));
   
   if(amount < 0) return;
   
@@ -245,15 +249,15 @@ i_bumpmap(i_img *im, i_img *bump, int channel, i_img_dim light_x, i_img_dim ligh
   double aX, aY, aL;
   double fZ;
   unsigned char px1, px2, py1, py2;
-  
+  dIMCTXim(im);
   i_img new_im;
 
-  mm_log((1, "i_bumpmap(im %p, add_im %p, channel %d, light(" i_DFp "), st %" i_DF ")\n",
+  im_log((aIMCTX, 1, "i_bumpmap(im %p, add_im %p, channel %d, light(" i_DFp "), st %" i_DF ")\n",
          im, bump, channel, i_DFcp(light_x, light_y), i_DFc(st)));
 
 
   if(channel >= bump->channels) {
-    mm_log((1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
+    im_log((aIMCTX, 1, "i_bumpmap: channel = %d while bump image only has %d channels\n", channel, bump->channels));
     return;
   }
 
@@ -427,12 +431,14 @@ i_bumpmap_complex(i_img *im,
   fvec R;         /* Reflection vector    */
   fvec V;         /* Vision vector        */
 
-  mm_log((1, "i_bumpmap_complex(im %p, bump %p, channel %d, t(" i_DFp 
+  dIMCTXim(im);
+
+  im_log((aIMCTX, 1, "i_bumpmap_complex(im %p, bump %p, channel %d, t(" i_DFp 
          "), Lx %.2f, Ly %.2f, Lz %.2f, cd %.2f, cs %.2f, n %.2f, Ia %p, Il %p, Is %p)\n",
          im, bump, channel, i_DFcp(tx, ty), Lx, Ly, Lz, cd, cs, n, Ia, Il, Is));
   
   if (channel >= bump->channels) {
-    mm_log((1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels));
+    im_log((aIMCTX, 1, "i_bumpmap_complex: channel = %d while bump image only has %d channels\n", channel, bump->channels));
     return;
   }
 
@@ -668,8 +674,9 @@ i_autolevels(i_img *im, float lsat, float usat, float skew) {
   i_img_dim gsum, gmin, gmax;
   i_img_dim bsum, bmin, bmax;
   i_img_dim rcl, rcu, gcl, gcu, bcl, bcu;
+  dIMCTXim(im);
 
-  mm_log((1,"i_autolevels(im %p, lsat %f,usat %f,skew %f)\n", im, lsat,usat,skew));
+  im_log((aIMCTX, 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;
@@ -924,11 +931,12 @@ i_gradgen(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *ival, int d
   size_t bytes;
 
   double *fdist;
+  dIMCTXim(im);
 
-  mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
+  im_log((aIMCTX, 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: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p])));
+    im_log((aIMCTX,1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p])));
     ICL_info(&ival[p]);
   }
 
@@ -964,7 +972,7 @@ i_gradgen(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *ival, int d
        fdist[p]  = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
-       i_fatal(3,"i_gradgen: Unknown distance measure\n");
+       im_fatal(aIMCTX, 3,"i_gradgen: Unknown distance measure\n");
       }
       cs += fdist[p];
     }
@@ -991,11 +999,12 @@ i_nearest_color_foo(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *i
   i_img_dim x, y;
   i_img_dim xsize    = im->xsize;
   i_img_dim ysize    = im->ysize;
+  dIMCTXim(im);
 
-  mm_log((1,"i_gradgen(im %p, num %d, xo %p, yo %p, ival %p, dmeasure %d)\n", im, num, xo, yo, ival, dmeasure));
+  im_log((aIMCTX,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: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p])));
+    im_log((aIMCTX, 1,"i_gradgen: p%d(" i_DFp ")\n", p, i_DFcp(xo[p], yo[p])));
     ICL_info(&ival[p]);
   }
 
@@ -1018,7 +1027,7 @@ i_nearest_color_foo(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *i
       mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
       break;
     default:
-      i_fatal(3,"i_nearest_color: Unknown distance measure\n");
+      im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n");
     }
 
     for(p = 1; p<num; p++) {
@@ -1035,7 +1044,7 @@ i_nearest_color_foo(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *i
        curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
-       i_fatal(3,"i_nearest_color: Unknown distance measure\n");
+       im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n");
       }
       if (curdist < mindist) {
        mindist = curdist;
@@ -1125,8 +1134,9 @@ i_nearest_color(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *oval,
   i_img_dim ysize    = im->ysize;
   int *cmatch;
   size_t ival_bytes, tval_bytes;
+  dIMCTXim(im);
 
-  mm_log((1,"i_nearest_color(im %p, num %d, xo %p, yo %p, oval %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure));
+  im_log((aIMCTX, 1,"i_nearest_color(im %p, num %d, xo %p, yo %p, oval %p, dmeasure %d)\n", im, num, xo, yo, oval, dmeasure));
 
   i_clear_error();
 
@@ -1179,7 +1189,7 @@ i_nearest_color(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *oval,
       mindist = i_max(xd*xd, yd*yd); /* manhattan distance */
       break;
     default:
-      i_fatal(3,"i_nearest_color: Unknown distance measure\n");
+      im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n");
     }
     
     for(p = 1; p<num; p++) {
@@ -1196,7 +1206,7 @@ i_nearest_color(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *oval,
        curdist = i_max(xd*xd, yd*yd); /* manhattan distance */
        break;
       default:
-       i_fatal(3,"i_nearest_color: Unknown distance measure\n");
+       im_fatal(aIMCTX, 3,"i_nearest_color: Unknown distance measure\n");
       }
       if (curdist < mindist) {
        mindist = curdist;
@@ -1320,6 +1330,7 @@ i_diff_image(i_img *im1, i_img *im2, double mindist) {
   i_img *out;
   int outchans, diffchans;
   i_img_dim xsize, ysize;
+  dIMCTXim(im1);
 
   i_clear_error();
   if (im1->channels != im2->channels) {
@@ -1642,6 +1653,7 @@ i_fountain(i_img *im, double xa, double ya, double xb, double yb,
   i_fountain_seg *my_segs;
   i_fill_combine_f combine_func = NULL;
   i_fill_combinef_f combinef_func = NULL;
+  dIMCTXim(im);
 
   i_clear_error();
 
diff --git a/flip.im b/flip.im
index 5bb1c74..d286b91 100644 (file)
--- a/flip.im
+++ b/flip.im
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 
 static void flip_h(i_img *im);
@@ -22,9 +23,10 @@ Returns 0 if parameters are invalid.
 
 undef_int
 i_flipxy(i_img *im, int direction) {
+  dIMCTXim(im);
   i_clear_error();
 
-  mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
+  im_log((aIMCTX, 1, "i_flipxy(im %p, direction %d)\n", im, direction ));
 
   if (!im)
     return 0;
@@ -43,10 +45,9 @@ i_flipxy(i_img *im, int direction) {
     break;
 
   default:
-    mm_log((1, "i_flipxy: direction is invalid\n" ));
-    i_push_errorf(0, "direction %d invalid", direction);
+    im_log((aIMCTX, 1, "i_flipxy: direction is invalid\n" ));
+    im_push_errorf(aIMCTX, 0, "direction %d invalid", direction);
     return 0;
-
   }
   return 1;
 }
diff --git a/font.c b/font.c
deleted file mode 100644 (file)
index a162f70..0000000
--- a/font.c
+++ /dev/null
@@ -1,1378 +0,0 @@
-#include "imager.h"
-#include "imrender.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#ifdef HAVE_LIBT1
-#endif
-
-
-/*
-=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, i_img_dim cords[BOUNDING_BOX_COUNT]);
-  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], utf8);
-  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
-
-=cut
-
-*/
-
-
-/* Truetype font support */
-#ifdef HAVE_LIBTT
-
-/* These are enabled by default when configuring Freetype 1.x
-   I haven't a clue how to reliably detect it at compile time.
-
-   We need a compilation probe in Makefile.PL
-*/
-#define FTXPOST 1
-#define FTXERR18 1
-
-#include <freetype.h>
-#define TT_CHC 5
-
-#ifdef FTXPOST
-#include <ftxpost.h>
-#endif
-
-#ifdef FTXERR18
-#include <ftxerr18.h>
-#endif
-
-/* some versions of FT1.x don't seem to define this - it's font defined
-   so it won't change */
-#ifndef TT_MS_LANGID_ENGLISH_GENERAL
-#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
-#endif
-
-/* convert a code point into an index in the glyph cache */
-#define TT_HASH(x) ((x) & 0xFF)
-
-typedef struct i_glyph_entry_ {
-  TT_Glyph glyph;
-  unsigned long ch;
-} i_tt_glyph_entry;
-
-#define TT_NOCHAR (~0UL)
-
-struct TT_Instancehandle_ {
-  TT_Instance instance;
-  TT_Instance_Metrics imetrics;
-  TT_Glyph_Metrics gmetrics[256];
-  i_tt_glyph_entry glyphs[256];
-  int smooth;
-  int order;
-  i_img_dim ptsize;
-};
-
-typedef struct TT_Instancehandle_ TT_Instancehandle;
-
-struct TT_Fonthandle_ {
-  TT_Face face;
-  TT_Face_Properties properties;
-  TT_Instancehandle instanceh[TT_CHC];
-  TT_CharMap char_map;
-#ifdef FTXPOST
-  int loaded_names;
-  TT_Error load_cond;
-#endif
-};
-
-/* Defines */
-
-#define USTRCT(x) ((x).z)
-#define TT_VALID( handle )  ( ( handle ).z != NULL )
-
-static void i_tt_push_error(TT_Error rc);
-
-/* Prototypes */
-
-static  int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
-static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim 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,i_img_dim x_off, i_img_dim y_off );
-static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
-static void 
-i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
-                   TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
-                   i_img_dim x_off, i_img_dim y_off, int smooth );
-static int
-i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
-                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
-                        char const* txt, size_t len, int smooth, int utf8 );
-static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
-static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
-static  int
-i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], 
-                double points, char const* txt, size_t len, int smooth, int utf8 );
-static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 );
-
-
-/* static globals needed */
-
-static int TT_initialized = 0;
-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
-*/
-
-static undef_int
-i_init_tt(void) {
-  TT_Error  error;
-  TT_Byte palette[] = { 0, 64, 127, 191, 255 };
-
-  i_clear_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",
-           (unsigned)error));
-    i_tt_push_error(error);
-    i_push_error(0, "Could not initialize freetype 1.x");
-    return(1);
-  }
-
-#ifdef FTXPOST
-  error = TT_Init_Post_Extension( engine );
-  if (error) {
-    mm_log((1, "Initialization of Post extension failed = 0x%x\n",
-           (unsigned)error));
-    
-    i_tt_push_error(error);
-    i_push_error(0, "Could not initialize FT 1.x POST extension");
-    return 1;
-  }
-#endif
-
-  error = TT_Set_Raster_Gray_Palette(engine, palette);
-  if (error) {
-    mm_log((1, "Initialization of gray levels failed = 0x%x\n",
-           (unsigned)error));
-    i_tt_push_error(error);
-    i_push_error(0, "Could not initialize FT 1.x POST extension");
-    return 1;
-  }
-
-  TT_initialized = 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, i_img_dim points, int smooth ) {
-  int i,idx;
-  TT_Error error;
-  
-  mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
-          handle, i_DFc(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 %p\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));
-
-    /* Free cached glyphs */
-    for(i=0;i<256;i++)
-      if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
-       TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
-
-    for(i=0;i<256;i++) {
-      handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
-      USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
-    }
-
-    /* Free instance if needed */
-    TT_Done_Instance( handle->instanceh[idx].instance );
-  }
-  
-  /* 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 %x.\n",
-           (unsigned)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++) {
-    handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
-    USTRCT(handle->instanceh[idx].glyphs[i].glyph)=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(const char *fontname) {
-  TT_Error error;
-  TT_Fonthandle *handle;
-  unsigned short i,n;
-  unsigned short platform,encoding;
-
-  if (!TT_initialized && i_init_tt()) {
-    i_push_error(0, "Could not initialize FT1 engine");
-    return NULL;
-  }
-
-  i_clear_error();
-  
-  mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
-  
-  /* allocate memory for the structure */
-  
-  handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
-
-  /* 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, 
-              (unsigned)error )); 
-    }
-    i_tt_push_error(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 ) ) {
-      mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
-              platform, encoding));
-      TT_Get_CharMap( handle->face, i, &(handle->char_map) );
-      break;
-    }
-  }
-  if (!USTRCT(handle->char_map) && n != 0) {
-    /* just use the first one */
-    TT_Get_CharMap( handle->face, 0, &(handle->char_map));
-  }
-
-  /* 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;
-  }
-
-#ifdef FTXPOST
-  handle->loaded_names = 0;
-#endif
-
-  mm_log((1,"i_tt_new <- %p\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, i_img_dim width, i_img_dim height, int smooth ) {
-
-  mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
-         ", smooth %d)\n", bit, i_DFc(width), i_DFc(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 */
-  }
-
-  /* rows can be 0 for some glyphs, for example ' ' */
-  if (bit->rows && bit->size / bit->rows != bit->cols) {
-    i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
-            bit->width, bit->rows);
-  }
-  
-  mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size ));
-
-  bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
-  if ( !bit->bitmap ) i_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,i_img_dim x_off, i_img_dim y_off ) {
-  i_img_dim  x,  y;
-  i_img_dim  x1, x2, y1, y2;
-  unsigned 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 = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
-    d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
-    
-    for ( x = x1; x < x2; ++x ) {
-      if (*s > *d)
-       *d = *s;
-      d++;
-      s++;
-    }
-  }
-}
-
-/* useful for debugging */
-#if 0
-
-static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
-  int x, y;
-  fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
-  for (y = 0; y < bit->rows; ++y) {
-    fprintf(out, "%2d:", y);
-    for (x = 0; x < bit->cols; ++x) {
-      if ((x & 7) == 0 && x) putc(' ', out);
-      fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
-    }
-    putc('\n', out);
-  }
-}
-
-#endif
-
-/* 
-=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 long j) {
-  unsigned short load_flags, code;
-  TT_Error error;
-
-  mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
-          handle,inst,j, (int)((j >= ' ' && 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[TT_HASH(j)].glyph)
-       && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
-    mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
-    return 1;
-  }
-
-  if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
-    /* clean up the entry */
-    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
-    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
-    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
-  }
-  
-  /* 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[TT_HASH(j)].glyph)) ) {
-    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
-    i_push_error(error, "TT_New_Glyph()");
-    return 0;
-  }
-  if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
-    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
-    /* Don't leak */
-    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
-    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
-    i_push_error(error, "TT_Load_Glyph()");
-    return 0;
-  }
-
-  /* At this point the glyph should be allocated and loaded */
-  handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
-
-  /* Next get the glyph metrics */
-  error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
-                                &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
-  if (error) {
-    mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
-    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
-    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
-    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
-    i_push_error(error, "TT_Get_Glyph_Metrics()");
-    return 0;
-  }
-
-  return 1;
-}
-
-/*
-=item i_tt_has_chars(handle, text, len, utf8, out)
-
-Check if the given characters are defined by the font.  Note that len
-is the number of bytes, not the number of characters (when utf8 is
-non-zero).
-
-Returns the number of characters that were checked.
-
-=cut
-*/
-
-size_t
-i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
-               char *out) {
-  size_t count = 0;
-  mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n", 
-          handle, text, (long)len, utf8));
-
-  while (len) {
-    unsigned long c;
-    int index;
-    if (utf8) {
-      c = i_utf8_advance(&text, &len);
-      if (c == ~0UL) {
-        i_push_error(0, "invalid UTF8 character");
-        return 0;
-      }
-    }
-    else {
-      c = (unsigned char)*text++;
-      --len;
-    }
-    
-    if (TT_VALID(handle->char_map)) {
-      index = TT_Char_Index(handle->char_map, c);
-    }
-    else {
-      index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
-      if (index >= handle->properties.num_Glyphs)
-        index = 0;
-    }
-    *out++ = index != 0;
-    ++count;
-  }
-
-  return count;
-}
-
-/* 
-=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 );
-  myfree( handle );
-  
-  /* 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, i_img_dim x_off, i_img_dim y_off, int smooth ) {
-  
-  mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n",
-         USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
-         i_DFc(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
-int
-i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
-                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
-                        char const* txt, size_t len, int smooth, int utf8 ) {
-  unsigned long j;
-  TT_F26Dot6 x,y;
-  
-  mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n",
-         handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
-  
-  /* 
-     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[4];
-  
-  while (len) {
-    if (utf8) {
-      j = i_utf8_advance(&txt, &len);
-      if (j == ~0UL) {
-        i_push_error(0, "invalid UTF8 character");
-        return 0;
-      }
-    }
-    else {
-      j = (unsigned char)*txt++;
-      --len;
-    }
-    if ( !i_tt_get_glyph(handle,inst,j) ) 
-      continue;
-    i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
-                       &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
-                       small_bit, x, y, smooth );
-    x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
-  }
-
-  return 1;
-}
-
-
-/*
- * 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, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) {
-  unsigned char *bmap;
-  i_img_dim x, y;
-  mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
-         im, bit, i_DFc(xb), i_DFc(yb), cl));
-  
-  bmap = bit->bitmap;
-
-  if ( smooth ) {
-
-    i_render r;
-    i_render_init(&r, im, bit->cols);
-    for(y=0;y<bit->rows;y++) {
-      i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
-    }
-    i_render_done(&r);
-  } else {
-    for(y=0;y<bit->rows;y++) {
-      unsigned mask = 0x80;
-      unsigned char *p = bmap + y * bit->cols;
-
-      for(x = 0; x < bit->width; x++) {
-       if (*p & mask) {
-         i_ppix(im, x+xb, y+yb, cl);
-       }
-       mask >>= 1;
-       if (!mask) {
-         mask = 0x80;
-         ++p;
-       }
-      }
-    }
-
-  }
-}
-
-
-/*
-=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, i_img_dim xb, i_img_dim yb, int channel, int smooth ) {
-  unsigned char *bmap;
-  i_color val;
-  int c;
-  i_img_dim x,y;
-  int old_mask = im->ch_mask;
-  im->ch_mask = 1 << channel;
-
-  mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
-         im, bit, i_DFc(xb), i_DFc(yb), channel));
-  
-  bmap = bit->bitmap;
-  
-  if ( smooth ) {
-    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
-      c = bmap[y*(bit->cols)+x];
-      val.channel[channel] = c;
-      i_ppix(im,x+xb,y+yb,&val);
-    }
-  } else {
-    for(y=0;y<bit->rows;y++) {
-      unsigned mask = 0x80;
-      unsigned char *p = bmap + y * bit->cols;
-
-      for(x=0;x<bit->width;x++) {
-       val.channel[channel] = (*p & mask) ? 255 : 0;
-       i_ppix(im,x+xb,y+yb,&val);
-       
-       mask >>= 1;
-       if (!mask) {
-         ++p;
-         mask = 0x80;
-       }
-      }
-    }
-  }
-  im->ch_mask = old_mask;
-}
-
-
-/* 
-=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, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) {
-  int inst;
-  i_img_dim 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 */
-  if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
-    return 0;
-    
-  
-  width  = cords[2]-cords[0];
-  height = cords[5]-cords[4];
-  
-  mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
-         i_DFc(width), i_DFc(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 );
-  
-  if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
-                               smooth, utf8 )) {
-    if ( smooth ) 
-      i_tt_done_raster_map( &small_bit );
-    return 0;
-  }
-
-  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, utf8)
-
-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, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) {
-
-  i_img_dim cords[BOUNDING_BOX_COUNT];
-  i_img_dim ascent, st_offset, y;
-  TT_Raster_Map bit;
-  
-  i_clear_error();
-  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
-  
-  ascent=cords[BBOX_ASCENT];
-  st_offset=cords[BBOX_NEG_WIDTH];
-  y = align ? yb-ascent : yb;
-
-  i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
-  i_tt_done_raster_map( &bit );
-
-  return 1;
-}
-
-
-/* 
-=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
-
-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, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) {
-  i_img_dim cords[BOUNDING_BOX_COUNT];
-  i_img_dim ascent, st_offset, y;
-  TT_Raster_Map bit;
-
-  i_clear_error();
-  
-  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
-  
-  ascent=cords[BBOX_ASCENT];
-  st_offset=cords[BBOX_NEG_WIDTH];
-  y = align ? yb-ascent : yb;
-
-  i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
-  i_tt_done_raster_map( &bit );
-
-  return 1;
-}
-
-
-/*
-=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
-
-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, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
-  int upm, casc, cdesc, first;
-  
-  int start    = 0;
-  i_img_dim width    = 0;
-  int gdescent = 0;
-  int gascent  = 0;
-  int descent  = 0;
-  int ascent   = 0;
-  int rightb   = 0;
-
-  unsigned long j;
-  unsigned char *ustr;
-  ustr=(unsigned char*)txt;
-
-  mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
-         handle, inst, (int)len, txt, (long)len, utf8));
-
-  upm     = handle->properties.header->Units_Per_EM;
-  gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
-  gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
-  
-  width   = 0;
-  start   = 0;
-  
-  mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
-
-  first=1;
-  while (len) {
-    if (utf8) {
-      j = i_utf8_advance(&txt, &len);
-      if (j == ~0UL) {
-        i_push_error(0, "invalid UTF8 character");
-        return 0;
-      }
-    }
-    else {
-      j = (unsigned char)*txt++;
-      --len;
-    }
-    if ( i_tt_get_glyph(handle,inst,j) ) {
-      TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
-      width += gm->advance   / 64;
-      casc   = (gm->bbox.yMax+63) / 64;
-      cdesc  = (gm->bbox.yMin-63) / 64;
-
-      mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
-              (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
-
-      if (first) {
-       start    = gm->bbox.xMin / 64;
-       ascent   = (gm->bbox.yMax+63) / 64;
-       descent  = (gm->bbox.yMin-63) / 64;
-       first = 0;
-      }
-      if (!len) { /* if at end of string */
-       /* the right-side bearing - in case the right-side of a 
-          character goes past the right of the advance width,
-          as is common for italic fonts
-       */
-       rightb = gm->advance - gm->bearingX 
-         - (gm->bbox.xMax - gm->bbox.xMin);
-       /* fprintf(stderr, "font info last: %d %d %d %d\n", 
-          gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
-      }
-
-      ascent  = (ascent  >  casc ?  ascent : casc );
-      descent = (descent < cdesc ? descent : cdesc);
-    }
-  }
-  
-  cords[BBOX_NEG_WIDTH]=start;
-  cords[BBOX_GLOBAL_DESCENT]=gdescent;
-  cords[BBOX_POS_WIDTH]=width;
-  if (rightb < 0)
-    cords[BBOX_POS_WIDTH] -= rightb / 64;
-  cords[BBOX_GLOBAL_ASCENT]=gascent;
-  cords[BBOX_DESCENT]=descent;
-  cords[BBOX_ASCENT]=ascent;
-  cords[BBOX_ADVANCE_WIDTH] = width;
-  cords[BBOX_RIGHT_BEARING] = rightb / 64;
-
-  return BBOX_RIGHT_BEARING + 1;
-}
-
-
-/*
-=item i_tt_bbox(handle, points, txt, len, cords, utf8)
-
-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, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
-  int inst;
-
-  i_clear_error();
-  mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
-         handle, points, (int)len, txt, (long)len, utf8));
-
-  if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
-    i_push_errorf(0, "i_tt_get_instance(%g)", points);
-    mm_log((1,"i_tt_text: get instance failed\n"));
-    return 0;
-  }
-
-  return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
-}
-
-/*
-=item i_tt_face_name(handle, name_buf, name_buf_size)
-
-Retrieve's the font's postscript name.
-
-This is complicated by the need to handle encodings and so on.
-
-=cut
- */
-size_t
-i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
-  TT_Face_Properties props;
-  int name_count;
-  int i;
-  TT_UShort platform_id, encoding_id, lang_id, name_id;
-  TT_UShort name_len;
-  TT_String *name;
-  int want_index = -1; /* an acceptable but not perfect name */
-  int score = 0;
-
-  i_clear_error();
-  
-  TT_Get_Face_Properties(handle->face, &props);
-  name_count = props.num_Names;
-  for (i = 0; i < name_count; ++i) {
-    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
-                   &name_id);
-
-    TT_Get_Name_String(handle->face, i, &name, &name_len);
-
-    if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
-        && name_id == TT_NAME_ID_PS_NAME) {
-      int might_want_index = -1;
-      int might_score = 0;
-      if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
-          ||
-          (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
-        /* exactly what we want */
-        want_index = i;
-        break;
-      }
-      
-      if (platform_id == TT_PLATFORM_MICROSOFT
-          && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
-        /* any english is good */
-        might_want_index = i;
-        might_score = 9;
-      }
-      /* there might be something in between */
-      else {
-        /* anything non-unicode is better than nothing */
-        might_want_index = i;
-        might_score = 1;
-      }
-      if (might_score > score) {
-        score = might_score;
-        want_index = might_want_index;
-      }
-    }
-  }
-
-  if (want_index != -1) {
-    TT_Get_Name_String(handle->face, want_index, &name, &name_len);
-    
-    strncpy(name_buf, name, name_buf_size);
-    name_buf[name_buf_size-1] = '\0';
-
-    return strlen(name) + 1;
-  }
-  else {
-    i_push_error(0, "no face name present");
-    return 0;
-  }
-}
-
-void i_tt_dump_names(TT_Fonthandle *handle) {
-  TT_Face_Properties props;
-  int name_count;
-  int i;
-  TT_UShort platform_id, encoding_id, lang_id, name_id;
-  TT_UShort name_len;
-  TT_String *name;
-  
-  TT_Get_Face_Properties(handle->face, &props);
-  name_count = props.num_Names;
-  for (i = 0; i < name_count; ++i) {
-    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
-                   &name_id);
-    TT_Get_Name_String(handle->face, i, &name, &name_len);
-
-    printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
-           encoding_id, lang_id, name_id);
-    if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
-      printf("(unicode)\n");
-    }
-    else {
-      printf("'%s'\n", name);
-    }
-  }
-  fflush(stdout);
-}
-
-size_t
-i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
-                 size_t name_buf_size) {
-#ifdef FTXPOST
-  TT_Error rc;
-  TT_String *psname;
-  TT_UShort index;
-
-  i_clear_error();
-
-  if (!handle->loaded_names) {
-    TT_Post post;
-    mm_log((1, "Loading PS Names"));
-    handle->load_cond = TT_Load_PS_Names(handle->face, &post);
-    ++handle->loaded_names;
-  }
-
-  if (handle->load_cond) {
-    i_push_errorf(handle->load_cond, "error loading names (%#x)",
-                 (unsigned)handle->load_cond);
-    return 0;
-  }
-  
-  index = TT_Char_Index(handle->char_map, ch);
-  if (!index) {
-    i_push_error(0, "no such character");
-    return 0;
-  }
-
-  rc = TT_Get_PS_Name(handle->face, index, &psname);
-
-  if (rc) {
-    i_push_error(rc, "error getting name");
-    return 0;
-  }
-
-  strncpy(name_buf, psname, name_buf_size);
-  name_buf[name_buf_size-1] = '\0';
-
-  return strlen(psname) + 1;
-#else
-  mm_log((1, "FTXPOST extension not enabled\n"));
-  i_clear_error();
-  i_push_error(0, "Use of FTXPOST extension disabled");
-
-  return 0;
-#endif
-}
-
-/*
-=item i_tt_push_error(code)
-
-Push an error message and code onto the Imager error stack.
-
-=cut
-*/
-static void
-i_tt_push_error(TT_Error rc) {
-#ifdef FTXERR18
-  TT_String const *msg = TT_ErrToString18(rc);
-
-  i_push_error(rc, msg);
-#else
-  i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
-#endif
-}
-
-#endif /* HAVE_LIBTT */
-
-
-/*
-=back
-
-=head1 AUTHOR
-
-Arnar M. Hrafnkelsson <addi@umich.edu>
-
-=head1 SEE ALSO
-
-Imager(3)
-
-=cut
-*/
diff --git a/fontft1.c b/fontft1.c
new file mode 100644 (file)
index 0000000..e0332f1
--- /dev/null
+++ b/fontft1.c
@@ -0,0 +1,1404 @@
+#include "imager.h"
+#include "imrender.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+
+/*
+=head1 NAME
+
+fontft1.c - Freetype 1.x font driver for Imager
+
+=head1 SYNOPSIS
+
+  handle = i_tt_new(path_to_ttf);
+  rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
+  i_tt_destroy(handle);
+
+  // and much more
+
+=head1 DESCRIPTION
+
+fontft1.c implements font creation, rendering, bounding box functions and
+more for Imager using Freetype 1.x.
+
+In general this driver should be ignored in favour of the FT2 driver.
+
+=head1 FUNCTION REFERENCE
+
+Some of these functions are internal.
+
+=over
+
+=cut
+
+*/
+
+
+/* Truetype font support */
+/* These are enabled by default when configuring Freetype 1.x
+   I haven't a clue how to reliably detect it at compile time.
+
+   We need a compilation probe in Makefile.PL
+*/
+#define FTXPOST 1
+#define FTXERR18 1
+
+#include <freetype.h>
+#define TT_CHC 5
+
+#ifdef FTXPOST
+#include <ftxpost.h>
+#endif
+
+#ifdef FTXERR18
+#include <ftxerr18.h>
+#endif
+
+/* some versions of FT1.x don't seem to define this - it's font defined
+   so it won't change */
+#ifndef TT_MS_LANGID_ENGLISH_GENERAL
+#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
+#endif
+
+static im_slot_t slot = -1;
+
+/* convert a code point into an index in the glyph cache */
+#define TT_HASH(x) ((x) & 0xFF)
+
+typedef struct {
+  int initialized;
+  TT_Engine engine;
+} i_tt_engine;
+
+typedef struct i_glyph_entry_ {
+  TT_Glyph glyph;
+  unsigned long ch;
+} i_tt_glyph_entry;
+
+#define TT_NOCHAR (~0UL)
+
+struct TT_Instancehandle_ {
+  TT_Instance instance;
+  TT_Instance_Metrics imetrics;
+  TT_Glyph_Metrics gmetrics[256];
+  i_tt_glyph_entry glyphs[256];
+  int smooth;
+  int order;
+  i_img_dim ptsize;
+};
+
+typedef struct TT_Instancehandle_ TT_Instancehandle;
+
+struct TT_Fonthandle_ {
+  TT_Face face;
+  TT_Face_Properties properties;
+  TT_Instancehandle instanceh[TT_CHC];
+  TT_CharMap char_map;
+#ifdef FTXPOST
+  int loaded_names;
+  TT_Error load_cond;
+#endif
+};
+
+/* Defines */
+
+#define USTRCT(x) ((x).z)
+#define TT_VALID( handle )  ( ( handle ).z != NULL )
+
+static void i_tt_push_error(TT_Error rc);
+static void i_tt_uninit(void *);
+
+/* Prototypes */
+
+static  int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
+static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim 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,i_img_dim x_off, i_img_dim y_off );
+static  int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
+static void 
+i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, 
+                   TT_Raster_Map *bit, TT_Raster_Map *small_bit, 
+                   i_img_dim x_off, i_img_dim y_off, int smooth );
+static int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
+                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
+                        char const* txt, size_t len, int smooth, int utf8 );
+static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
+static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
+static  int
+i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6], 
+                double points, char const* txt, size_t len, int smooth, int utf8 );
+static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 );
+
+
+/* static globals needed */
+
+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
+ */
+
+void
+i_tt_start(void) {
+  im_context_t ctx = im_get_context();
+  if (slot == -1)
+    slot = im_context_slot_new(i_tt_uninit);
+}
+
+
+/*
+=item init_tt()
+
+Initializes the freetype font rendering engine (if needed)
+
+=cut
+*/
+
+static i_tt_engine *
+i_init_tt(void) {
+  TT_Error  error;
+  im_context_t ctx = im_get_context();
+  TT_Byte palette[] = { 0, 64, 127, 191, 255 };
+  i_tt_engine *result = im_context_slot_get(ctx, slot);
+
+  i_clear_error();
+
+  if (result == NULL) {
+    result = mymalloc(sizeof(i_tt_engine));
+    memset(result, 0, sizeof(*result));
+    im_context_slot_set(ctx, slot, result);
+    mm_log((1, "allocated FT1 state %p\n", result));
+  }
+
+  mm_log((1,"init_tt()\n"));
+
+  if (result->initialized)
+    return result;
+
+  error = TT_Init_FreeType( &result->engine );
+  if ( error ){
+    mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
+           (unsigned)error));
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize freetype 1.x");
+    return NULL;
+  }
+
+#ifdef FTXPOST
+  error = TT_Init_Post_Extension( result->engine );
+  if (error) {
+    mm_log((1, "Initialization of Post extension failed = 0x%x\n",
+           (unsigned)error));
+    
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize FT 1.x POST extension");
+    return NULL;
+  }
+#endif
+
+  error = TT_Set_Raster_Gray_Palette(result->engine, palette);
+  if (error) {
+    mm_log((1, "Initialization of gray levels failed = 0x%x\n",
+           (unsigned)error));
+    i_tt_push_error(error);
+    i_push_error(0, "Could not initialize FT 1.x POST extension");
+    return NULL;
+  }
+
+  mm_log((1, "initialized FT1 state %p\n", result));
+
+  result->initialized = 1;
+
+  return result;
+}
+
+static void
+i_tt_uninit(void *p) {
+  i_tt_engine *tteng = p;
+
+  if (tteng->initialized) {
+    mm_log((1, "finalizing FT1 state %p\n", tteng));
+    TT_Done_FreeType(tteng->engine);
+  }
+  mm_log((1, "freeing FT1 state %p\n", tteng));
+  myfree(tteng);
+}
+
+/* 
+=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, i_img_dim points, int smooth ) {
+  int i,idx;
+  TT_Error error;
+  
+  mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
+          handle, i_DFc(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 %p\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));
+
+    /* Free cached glyphs */
+    for(i=0;i<256;i++)
+      if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
+       TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
+
+    for(i=0;i<256;i++) {
+      handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+      USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
+    }
+
+    /* Free instance if needed */
+    TT_Done_Instance( handle->instanceh[idx].instance );
+  }
+  
+  /* 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 %x.\n",
+           (unsigned)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++) {
+    handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
+    USTRCT(handle->instanceh[idx].glyphs[i].glyph)=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(const char *fontname) {
+  TT_Error error;
+  TT_Fonthandle *handle;
+  unsigned short i,n;
+  unsigned short platform,encoding;
+  i_tt_engine *tteng;
+
+  if ((tteng = i_init_tt()) == NULL) {
+    i_push_error(0, "Could not initialize FT1 engine");
+    return NULL;
+  }
+
+  i_clear_error();
+  
+  mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
+  
+  /* allocate memory for the structure */
+  
+  handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
+
+  /* load the typeface */
+  error = TT_Open_Face( tteng->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, 
+              (unsigned)error )); 
+    }
+    i_tt_push_error(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 ) ) {
+      mm_log((2,"i_tt_new - found char map platform %u encoding %u\n", 
+              platform, encoding));
+      TT_Get_CharMap( handle->face, i, &(handle->char_map) );
+      break;
+    }
+  }
+  if (!USTRCT(handle->char_map) && n != 0) {
+    /* just use the first one */
+    TT_Get_CharMap( handle->face, 0, &(handle->char_map));
+  }
+
+  /* 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;
+  }
+
+#ifdef FTXPOST
+  handle->loaded_names = 0;
+#endif
+
+  mm_log((1,"i_tt_new <- %p\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, i_img_dim width, i_img_dim height, int smooth ) {
+
+  mm_log((1,"i_tt_init_raster_map( bit %p, width %" i_DF ", height %" i_DF
+         ", smooth %d)\n", bit, i_DFc(width), i_DFc(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 */
+  }
+
+  /* rows can be 0 for some glyphs, for example ' ' */
+  if (bit->rows && bit->size / bit->rows != bit->cols) {
+    i_fatal(0, "Integer overflow calculating bitmap size (%d, %d)\n",
+            bit->width, bit->rows);
+  }
+  
+  mm_log((1,"i_tt_init_raster_map: bit->width %d, bit->cols %d, bit->rows %d, bit->size %ld)\n", bit->width, bit->cols, bit->rows, bit->size ));
+
+  bit->bitmap = (void *) mymalloc( bit->size ); /* checked 6Nov05 tonyc */
+  if ( !bit->bitmap ) i_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,i_img_dim x_off, i_img_dim y_off ) {
+  i_img_dim  x,  y;
+  i_img_dim  x1, x2, y1, y2;
+  unsigned 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 = ( (unsigned char*)src->bitmap ) + y * src->cols + x1;
+    d = ( (unsigned char*)dst->bitmap ) + ( y + y_off ) * dst->cols + x1 + x_off;
+    
+    for ( x = x1; x < x2; ++x ) {
+      if (*s > *d)
+       *d = *s;
+      d++;
+      s++;
+    }
+  }
+}
+
+/* useful for debugging */
+#if 0
+
+static void dump_raster_map(FILE *out, TT_Raster_Map *bit ) {
+  int x, y;
+  fprintf(out, "cols %d rows %d  flow %d\n", bit->cols, bit->rows, bit->flow);
+  for (y = 0; y < bit->rows; ++y) {
+    fprintf(out, "%2d:", y);
+    for (x = 0; x < bit->cols; ++x) {
+      if ((x & 7) == 0 && x) putc(' ', out);
+      fprintf(out, "%02x", ((unsigned char *)bit->bitmap)[y*bit->cols+x]);
+    }
+    putc('\n', out);
+  }
+}
+
+#endif
+
+/* 
+=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 long j) {
+  unsigned short load_flags, code;
+  TT_Error error;
+
+  mm_log((1, "i_tt_get_glyph(handle %p, inst %d, j %lu (%c))\n",
+          handle,inst,j, (int)((j >= ' ' && 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[TT_HASH(j)].glyph)
+       && handle->instanceh[inst].glyphs[TT_HASH(j)].ch == j) {
+    mm_log((1,"i_tt_get_glyph: %lu in cache\n",j));
+    return 1;
+  }
+
+  if ( TT_VALID(handle->instanceh[inst].glyphs[TT_HASH(j)].glyph) ) {
+    /* clean up the entry */
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+  }
+  
+  /* 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[TT_HASH(j)].glyph)) ) {
+    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+    i_push_error(error, "TT_New_Glyph()");
+    return 0;
+  }
+  if ( (error = TT_Load_Glyph( handle->instanceh[inst].instance, handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, code, load_flags)) ) {
+    mm_log((1, "Cannot allocate and load glyph: error %#x.\n", (unsigned)error ));
+    /* Don't leak */
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    i_push_error(error, "TT_Load_Glyph()");
+    return 0;
+  }
+
+  /* At this point the glyph should be allocated and loaded */
+  handle->instanceh[inst].glyphs[TT_HASH(j)].ch = j;
+
+  /* Next get the glyph metrics */
+  error = TT_Get_Glyph_Metrics( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
+                                &handle->instanceh[inst].gmetrics[TT_HASH(j)] );
+  if (error) {
+    mm_log((1, "TT_Get_Glyph_Metrics: error %#x.\n", (unsigned)error ));
+    TT_Done_Glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph );
+    USTRCT( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph ) = NULL;
+    handle->instanceh[inst].glyphs[TT_HASH(j)].ch = TT_NOCHAR;
+    i_push_error(error, "TT_Get_Glyph_Metrics()");
+    return 0;
+  }
+
+  return 1;
+}
+
+/*
+=item i_tt_has_chars(handle, text, len, utf8, out)
+
+Check if the given characters are defined by the font.  Note that len
+is the number of bytes, not the number of characters (when utf8 is
+non-zero).
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+
+size_t
+i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
+               char *out) {
+  size_t count = 0;
+  mm_log((1, "i_tt_has_chars(handle %p, text %p, len %ld, utf8 %d)\n", 
+          handle, text, (long)len, utf8));
+
+  while (len) {
+    unsigned long c;
+    int index;
+    if (utf8) {
+      c = i_utf8_advance(&text, &len);
+      if (c == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      c = (unsigned char)*text++;
+      --len;
+    }
+    
+    if (TT_VALID(handle->char_map)) {
+      index = TT_Char_Index(handle->char_map, c);
+    }
+    else {
+      index = (c - ' ' + 1) < 0 ? 0 : (c - ' ' + 1);
+      if (index >= handle->properties.num_Glyphs)
+        index = 0;
+    }
+    *out++ = index != 0;
+    ++count;
+  }
+
+  return count;
+}
+
+/* 
+=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 );
+  myfree( handle );
+  
+  /* 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, i_img_dim x_off, i_img_dim y_off, int smooth ) {
+  
+  mm_log((1,"i_tt_render_glyph(glyph %p, gmetrics %p, bit %p, small_bit %p, x_off %" i_DF ", y_off %" i_DF ", smooth %d)\n",
+         USTRCT(glyph), gmetrics, bit, small_bit, i_DFc(x_off),
+         i_DFc(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
+int
+i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
+                        TT_Raster_Map *small_bit, i_img_dim cords[6], 
+                        char const* txt, size_t len, int smooth, int utf8 ) {
+  unsigned long j;
+  TT_F26Dot6 x,y;
+  
+  mm_log((1,"i_tt_render_all_glyphs( handle %p, inst %d, bit %p, small_bit %p, txt '%.*s', len %ld, smooth %d, utf8 %d)\n",
+         handle, inst, bit, small_bit, (int)len, txt, (long)len, smooth, utf8));
+  
+  /* 
+     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[4];
+  
+  while (len) {
+    if (utf8) {
+      j = i_utf8_advance(&txt, &len);
+      if (j == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      j = (unsigned char)*txt++;
+      --len;
+    }
+    if ( !i_tt_get_glyph(handle,inst,j) ) 
+      continue;
+    i_tt_render_glyph( handle->instanceh[inst].glyphs[TT_HASH(j)].glyph, 
+                       &handle->instanceh[inst].gmetrics[TT_HASH(j)], bit, 
+                       small_bit, x, y, smooth );
+    x += handle->instanceh[inst].gmetrics[TT_HASH(j)].advance / 64;
+  }
+
+  return 1;
+}
+
+
+/*
+ * 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, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth ) {
+  unsigned char *bmap;
+  i_img_dim x, y;
+  mm_log((1,"i_tt_dump_raster_map2(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", cl %p)\n",
+         im, bit, i_DFc(xb), i_DFc(yb), cl));
+  
+  bmap = bit->bitmap;
+
+  if ( smooth ) {
+
+    i_render r;
+    i_render_init(&r, im, bit->cols);
+    for(y=0;y<bit->rows;y++) {
+      i_render_color(&r, xb, yb+y, bit->cols, bmap + y*bit->cols, cl);
+    }
+    i_render_done(&r);
+  } else {
+    for(y=0;y<bit->rows;y++) {
+      unsigned mask = 0x80;
+      unsigned char *p = bmap + y * bit->cols;
+
+      for(x = 0; x < bit->width; x++) {
+       if (*p & mask) {
+         i_ppix(im, x+xb, y+yb, cl);
+       }
+       mask >>= 1;
+       if (!mask) {
+         mask = 0x80;
+         ++p;
+       }
+      }
+    }
+
+  }
+}
+
+
+/*
+=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, i_img_dim xb, i_img_dim yb, int channel, int smooth ) {
+  unsigned char *bmap;
+  i_color val;
+  int c;
+  i_img_dim x,y;
+  int old_mask = im->ch_mask;
+  im->ch_mask = 1 << channel;
+
+  mm_log((1,"i_tt_dump_raster_channel(im %p, bit %p, xb %" i_DF ", yb %" i_DF ", channel %d)\n",
+         im, bit, i_DFc(xb), i_DFc(yb), channel));
+  
+  bmap = bit->bitmap;
+  
+  if ( smooth ) {
+    for(y=0;y<bit->rows;y++) for(x=0;x<bit->width;x++) {
+      c = bmap[y*(bit->cols)+x];
+      val.channel[channel] = c;
+      i_ppix(im,x+xb,y+yb,&val);
+    }
+  } else {
+    for(y=0;y<bit->rows;y++) {
+      unsigned mask = 0x80;
+      unsigned char *p = bmap + y * bit->cols;
+
+      for(x=0;x<bit->width;x++) {
+       val.channel[channel] = (*p & mask) ? 255 : 0;
+       i_ppix(im,x+xb,y+yb,&val);
+       
+       mask >>= 1;
+       if (!mask) {
+         ++p;
+         mask = 0x80;
+       }
+      }
+    }
+  }
+  im->ch_mask = old_mask;
+}
+
+
+/* 
+=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, i_img_dim cords[6], double points, char const* txt, size_t len, int smooth, int utf8 ) {
+  int inst;
+  i_img_dim 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 */
+  if (!i_tt_bbox_inst( handle, inst, txt, len, cords, utf8 ))
+    return 0;
+    
+  
+  width  = cords[2]-cords[0];
+  height = cords[5]-cords[4];
+  
+  mm_log((1,"i_tt_rasterize: width=%" i_DF ", height=%" i_DF "\n",
+         i_DFc(width), i_DFc(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 );
+  
+  if (!i_tt_render_all_glyphs( handle, inst, bit, &small_bit, cords, txt, len, 
+                               smooth, utf8 )) {
+    if ( smooth ) 
+      i_tt_done_raster_map( &small_bit );
+    return 0;
+  }
+
+  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, utf8)
+
+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, i_img_dim xb, i_img_dim yb, int channel, double points, char const* txt, size_t len, int smooth, int utf8, int align ) {
+
+  i_img_dim cords[BOUNDING_BOX_COUNT];
+  i_img_dim ascent, st_offset, y;
+  TT_Raster_Map bit;
+  
+  i_clear_error();
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
+  
+  ascent=cords[BBOX_ASCENT];
+  st_offset=cords[BBOX_NEG_WIDTH];
+  y = align ? yb-ascent : yb;
+
+  i_tt_dump_raster_map_channel( im, &bit, xb-st_offset , y, channel, smooth );
+  i_tt_done_raster_map( &bit );
+
+  return 1;
+}
+
+
+/* 
+=item i_tt_text(handle, im, xb, yb, cl, points, txt, len, smooth, utf8) 
+
+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, i_img_dim xb, i_img_dim yb, const i_color *cl, double points, char const* txt, size_t len, int smooth, int utf8, int align) {
+  i_img_dim cords[BOUNDING_BOX_COUNT];
+  i_img_dim ascent, st_offset, y;
+  TT_Raster_Map bit;
+
+  i_clear_error();
+  
+  if (! i_tt_rasterize( handle, &bit, cords, points, txt, len, smooth, utf8 ) ) return 0;
+  
+  ascent=cords[BBOX_ASCENT];
+  st_offset=cords[BBOX_NEG_WIDTH];
+  y = align ? yb-ascent : yb;
+
+  i_tt_dump_raster_map2( im, &bit, xb+st_offset, y, cl, smooth ); 
+  i_tt_done_raster_map( &bit );
+
+  return 1;
+}
+
+
+/*
+=item i_tt_bbox_inst(handle, inst, txt, len, cords, utf8) 
+
+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, size_t len, i_img_dim cords[BOUNDING_BOX_COUNT], int utf8 ) {
+  int upm, casc, cdesc, first;
+  
+  int start    = 0;
+  i_img_dim width    = 0;
+  int gdescent = 0;
+  int gascent  = 0;
+  int descent  = 0;
+  int ascent   = 0;
+  int rightb   = 0;
+
+  unsigned long j;
+  unsigned char *ustr;
+  ustr=(unsigned char*)txt;
+
+  mm_log((1,"i_tt_box_inst(handle %p,inst %d,txt '%.*s', len %ld, utf8 %d)\n",
+         handle, inst, (int)len, txt, (long)len, utf8));
+
+  upm     = handle->properties.header->Units_Per_EM;
+  gascent  = ( handle->properties.horizontal->Ascender  * handle->instanceh[inst].imetrics.y_ppem + upm - 1) / upm;
+  gdescent = ( handle->properties.horizontal->Descender * handle->instanceh[inst].imetrics.y_ppem - upm + 1) / upm;
+  
+  width   = 0;
+  start   = 0;
+  
+  mm_log((1, "i_tt_box_inst: gascent=%d gdescent=%d\n", gascent, gdescent));
+
+  first=1;
+  while (len) {
+    if (utf8) {
+      j = i_utf8_advance(&txt, &len);
+      if (j == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      j = (unsigned char)*txt++;
+      --len;
+    }
+    if ( i_tt_get_glyph(handle,inst,j) ) {
+      TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + TT_HASH(j);
+      width += gm->advance   / 64;
+      casc   = (gm->bbox.yMax+63) / 64;
+      cdesc  = (gm->bbox.yMin-63) / 64;
+
+      mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", 
+              (int)((j >= ' ' && j <= '~') ? j : '.'), casc, cdesc));
+
+      if (first) {
+       start    = gm->bbox.xMin / 64;
+       ascent   = (gm->bbox.yMax+63) / 64;
+       descent  = (gm->bbox.yMin-63) / 64;
+       first = 0;
+      }
+      if (!len) { /* if at end of string */
+       /* the right-side bearing - in case the right-side of a 
+          character goes past the right of the advance width,
+          as is common for italic fonts
+       */
+       rightb = gm->advance - gm->bearingX 
+         - (gm->bbox.xMax - gm->bbox.xMin);
+       /* fprintf(stderr, "font info last: %d %d %d %d\n", 
+          gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
+      }
+
+      ascent  = (ascent  >  casc ?  ascent : casc );
+      descent = (descent < cdesc ? descent : cdesc);
+    }
+  }
+  
+  cords[BBOX_NEG_WIDTH]=start;
+  cords[BBOX_GLOBAL_DESCENT]=gdescent;
+  cords[BBOX_POS_WIDTH]=width;
+  if (rightb < 0)
+    cords[BBOX_POS_WIDTH] -= rightb / 64;
+  cords[BBOX_GLOBAL_ASCENT]=gascent;
+  cords[BBOX_DESCENT]=descent;
+  cords[BBOX_ASCENT]=ascent;
+  cords[BBOX_ADVANCE_WIDTH] = width;
+  cords[BBOX_RIGHT_BEARING] = rightb / 64;
+
+  return BBOX_RIGHT_BEARING + 1;
+}
+
+
+/*
+=item i_tt_bbox(handle, points, txt, len, cords, utf8)
+
+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, double points,const char *txt,size_t len,i_img_dim cords[6], int utf8) {
+  int inst;
+
+  i_clear_error();
+  mm_log((1,"i_tt_box(handle %p,points %f,txt '%.*s', len %ld, utf8 %d)\n",
+         handle, points, (int)len, txt, (long)len, utf8));
+
+  if ( (inst=i_tt_get_instance(handle,points,-1)) < 0) {
+    i_push_errorf(0, "i_tt_get_instance(%g)", points);
+    mm_log((1,"i_tt_text: get instance failed\n"));
+    return 0;
+  }
+
+  return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
+}
+
+/*
+=item i_tt_face_name(handle, name_buf, name_buf_size)
+
+Retrieve's the font's postscript name.
+
+This is complicated by the need to handle encodings and so on.
+
+=cut
+ */
+size_t
+i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+  TT_Face_Properties props;
+  int name_count;
+  int i;
+  TT_UShort platform_id, encoding_id, lang_id, name_id;
+  TT_UShort name_len;
+  TT_String *name;
+  int want_index = -1; /* an acceptable but not perfect name */
+  int score = 0;
+
+  i_clear_error();
+  
+  TT_Get_Face_Properties(handle->face, &props);
+  name_count = props.num_Names;
+  for (i = 0; i < name_count; ++i) {
+    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
+                   &name_id);
+
+    TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+    if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
+        && name_id == TT_NAME_ID_PS_NAME) {
+      int might_want_index = -1;
+      int might_score = 0;
+      if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
+          ||
+          (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
+        /* exactly what we want */
+        want_index = i;
+        break;
+      }
+      
+      if (platform_id == TT_PLATFORM_MICROSOFT
+          && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
+        /* any english is good */
+        might_want_index = i;
+        might_score = 9;
+      }
+      /* there might be something in between */
+      else {
+        /* anything non-unicode is better than nothing */
+        might_want_index = i;
+        might_score = 1;
+      }
+      if (might_score > score) {
+        score = might_score;
+        want_index = might_want_index;
+      }
+    }
+  }
+
+  if (want_index != -1) {
+    TT_Get_Name_String(handle->face, want_index, &name, &name_len);
+    
+    strncpy(name_buf, name, name_buf_size);
+    name_buf[name_buf_size-1] = '\0';
+
+    return strlen(name) + 1;
+  }
+  else {
+    i_push_error(0, "no face name present");
+    return 0;
+  }
+}
+
+void i_tt_dump_names(TT_Fonthandle *handle) {
+  TT_Face_Properties props;
+  int name_count;
+  int i;
+  TT_UShort platform_id, encoding_id, lang_id, name_id;
+  TT_UShort name_len;
+  TT_String *name;
+  
+  TT_Get_Face_Properties(handle->face, &props);
+  name_count = props.num_Names;
+  for (i = 0; i < name_count; ++i) {
+    TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id, 
+                   &name_id);
+    TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+    printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
+           encoding_id, lang_id, name_id);
+    if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
+      printf("(unicode)\n");
+    }
+    else {
+      printf("'%s'\n", name);
+    }
+  }
+  fflush(stdout);
+}
+
+size_t
+i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf, 
+                 size_t name_buf_size) {
+#ifdef FTXPOST
+  TT_Error rc;
+  TT_String *psname;
+  TT_UShort index;
+
+  i_clear_error();
+
+  if (!handle->loaded_names) {
+    TT_Post post;
+    mm_log((1, "Loading PS Names"));
+    handle->load_cond = TT_Load_PS_Names(handle->face, &post);
+    ++handle->loaded_names;
+  }
+
+  if (handle->load_cond) {
+    i_push_errorf(handle->load_cond, "error loading names (%#x)",
+                 (unsigned)handle->load_cond);
+    return 0;
+  }
+  
+  index = TT_Char_Index(handle->char_map, ch);
+  if (!index) {
+    i_push_error(0, "no such character");
+    return 0;
+  }
+
+  rc = TT_Get_PS_Name(handle->face, index, &psname);
+
+  if (rc) {
+    i_push_error(rc, "error getting name");
+    return 0;
+  }
+
+  strncpy(name_buf, psname, name_buf_size);
+  name_buf[name_buf_size-1] = '\0';
+
+  return strlen(psname) + 1;
+#else
+  mm_log((1, "FTXPOST extension not enabled\n"));
+  i_clear_error();
+  i_push_error(0, "Use of FTXPOST extension disabled");
+
+  return 0;
+#endif
+}
+
+/*
+=item i_tt_push_error(code)
+
+Push an error message and code onto the Imager error stack.
+
+=cut
+*/
+static void
+i_tt_push_error(TT_Error rc) {
+#ifdef FTXERR18
+  TT_String const *msg = TT_ErrToString18(rc);
+
+  i_push_error(rc, msg);
+#else
+  i_push_errorf(rc, "Error code 0x%04x", (unsigned)rc);
+#endif
+}
+
+
+/*
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+=head1 SEE ALSO
+
+Imager(3)
+
+=cut
+*/
index 45fc02d..7e93769 100644 (file)
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imager.h"
 #include <math.h>
 
@@ -25,8 +26,9 @@ i_gaussian(i_img *im, double stddev) {
   double res[MAXCHANNELS];
   i_img *timg;
   int radius, diameter;
+  dIMCTXim(im);
 
-  mm_log((1,"i_gaussian(im %p, stdev %.2f)\n",im,stddev));
+  im_log((aIMCTX, 1,"i_gaussian(im %p, stdev %.2f)\n",im,stddev));
   i_clear_error();
 
   if (stddev <= 0) {
index c4b04cc..460a20f 100644 (file)
--- a/hlines.c
+++ b/hlines.c
@@ -1,3 +1,4 @@
+#define IMAGER_NO_CONTEXT
 #include "imageri.h"
 #include <stdlib.h>
 
@@ -68,7 +69,8 @@ i_int_init_hlines(
   size_t bytes = count_y * sizeof(i_int_hline_entry *);
 
   if (bytes / count_y != sizeof(i_int_hline_entry *)) {
-    i_fatal(3, "integer overflow calculating memory allocation\n");
+    dIMCTX;
+    im_fatal(aIMCTX, 3, "integer overflow calculating memory allocation\n");
   }
 
   hlines->start_y = start_y;
@@ -111,7 +113,8 @@ i_int_hlines_add(i_int_hlines *hlines, i_img_dim y, i_img_dim x, i_img_dim width
   i_img_dim x_limit = x + width;
 
   if (width < 0) {
-    i_fatal(3, "negative width %d passed to i_int_hlines_add\n", width);
+    dIMCTX;
+    im_fatal(aIMCTX, 3, "negative width %d passed to i_int_hlines_add\n", width);
   }
 
   /* just return if out of range */
diff --git a/image.c b/image.c
index a8045f2..10228d1 100644 (file)
--- a/image.c
+++ b/image.c
@@ -1,3 +1,5 @@
+#define IMAGER_NO_CONTEXT
+
 #include "imager.h"
 #include "imageri.h"
 
@@ -12,7 +14,7 @@ image.c - implements most of the basic functions of Imager and much of the rest
   i_color *c;
   c = i_color_new(red, green, blue, alpha);
   ICL_DESTROY(c);
-  i = i_img_new();
+  i = i_img_8_new();
   i_img_destroy(i);
   // and much more
 
@@ -30,6 +32,8 @@ Some of these functions are internal.
 =cut
 */
 
+im_context_t (*im_get_context)(void) = NULL;
+
 #define XAXIS 0
 #define YAXIS 1
 #define XYAXIS 2
@@ -40,8 +44,11 @@ Some of these functions are internal.
 void i_linker_bug_fake(void) { ceil(1); }
 
 /*
-=item i_img_alloc()
+=item im_img_alloc(aIMCTX)
+X<im_img_alloc API>X<i_img_alloc API>
 =category Image Implementation
+=synopsis i_img *im = im_img_alloc(aIMCTX);
+=synopsis i_img *im = i_img_alloc();
 
 Allocates a new i_img structure.
 
@@ -70,25 +77,29 @@ object.
 */
 
 i_img *
-i_img_alloc(void) {
+im_img_alloc(pIMCTX) {
   return mymalloc(sizeof(i_img));
 }
 
 /*
-=item i_img_init(C<img>)
+=item im_img_init(aIMCTX, image)
+X<im_img_init API>X<i_img_init API>
 =category Image Implementation
+=synopsis im_img_init(aIMCTX, im);
+=synopsis i_img_init(im);
 
 Imager internal initialization of images.
 
-Currently this does very little, in the future it may be used to
-support threads, or color profiles.
+See L</im_img_alloc(aIMCTX)> for more information.
 
 =cut
 */
 
 void
-i_img_init(i_img *img) {
+im_img_init(pIMCTX, i_img *img) {
   img->im_data = NULL;
+  img->context = aIMCTX;
+  im_context_refinc(aIMCTX, "img_init");
 }
 
 /* 
@@ -107,15 +118,16 @@ Return a new color object with values passed to it.
 i_color *
 ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
   i_color *cl = NULL;
+  dIMCTX;
 
-  mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
+  im_log((aIMCTX,1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_color))) == NULL) i_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_color))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
   cl->rgba.a = a;
-  mm_log((1,"(%p) <- ICL_new_internal\n",cl));
+  im_log((aIMCTX,1,"(%p) <- ICL_new_internal\n",cl));
   return cl;
 }
 
@@ -136,15 +148,16 @@ ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a
 
 i_color *
 ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
-  mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
+  dIMCTX;
+  im_log((aIMCTX,1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
   if (cl == NULL)
     if ( (cl=mymalloc(sizeof(i_color))) == NULL)
-      i_fatal(2,"malloc() error\n");
+      im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r=r;
   cl->rgba.g=g;
   cl->rgba.b=b;
   cl->rgba.a=a;
-  mm_log((1,"(%p) <- ICL_set_internal\n",cl));
+  im_log((aIMCTX,1,"(%p) <- ICL_set_internal\n",cl));
   return cl;
 }
 
@@ -182,8 +195,9 @@ Dump color information to log - strictly for debugging.
 
 void
 ICL_info(i_color const *cl) {
-  mm_log((1,"i_color_info(cl* %p)\n",cl));
-  mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
+  dIMCTX;
+  im_log((aIMCTX, 1,"i_color_info(cl* %p)\n",cl));
+  im_log((aIMCTX, 1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
 }
 
 /* 
@@ -198,7 +212,8 @@ Destroy ancillary data for Color object.
 
 void
 ICL_DESTROY(i_color *cl) {
-  mm_log((1,"ICL_DESTROY(cl* %p)\n",cl));
+  dIMCTX;
+  im_log((aIMCTX, 1,"ICL_DESTROY(cl* %p)\n",cl));
   myfree(cl);
 }
 
@@ -209,15 +224,16 @@ ICL_DESTROY(i_color *cl) {
 */
 i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
   i_fcolor *cl = NULL;
+  dIMCTX;
 
-  mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
+  im_log((aIMCTX, 1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) i_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
   cl->rgba.a = a;
-  mm_log((1,"(%p) <- i_fcolor_new\n",cl));
+  im_log((aIMCTX, 1,"(%p) <- i_fcolor_new\n",cl));
 
   return cl;
 }
@@ -243,7 +259,8 @@ Free image data.
 
 void
 i_img_exorcise(i_img *im) {
-  mm_log((1,"i_img_exorcise(im* %p)\n",im));
+  dIMCTXim(im);
+  im_log((aIMCTX,1,"i_img_exorcise(im* %p)\n",im));
   i_tags_destroy(&im->tags);
   if (im->i_f_destroy)
     (im->i_f_destroy)(im);
@@ -269,9 +286,11 @@ Destroy an image object
 
 void
 i_img_destroy(i_img *im) {
-  mm_log((1,"i_img_destroy(im %p)\n",im));
+  dIMCTXim(im);
+  im_log((aIMCTX, 1,"i_img_destroy(im %p)\n",im));
   i_img_exorcise(im);
   if (im) { myfree(im); }
+  im_context_refdec(aIMCTX, "img_destroy");
 }
 
 /* 
@@ -297,12 +316,13 @@ info is an array of 4 integers with the following values:
 
 void
 i_img_info(i_img *im, i_img_dim *info) {
-  mm_log((1,"i_img_info(im %p)\n",im));
+  dIMCTXim(im);
+  im_log((aIMCTX,1,"i_img_info(im %p)\n",im));
   if (im != NULL) {
-    mm_log((1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d "
+    im_log((aIMCTX,1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d "
            "mask=%ud\n",
            i_DFc(im->xsize), i_DFc(im->ysize), im->channels,im->ch_mask));
-    mm_log((1,"i_img_info: idata=%p\n",im->idata));
+    im_log((aIMCTX,1,"i_img_info: idata=%p\n",im->idata));
     info[0] = im->xsize;
     info[1] = im->ysize;
     info[2] = im->channels;
@@ -401,8 +421,9 @@ i_copyto_trans(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img
   i_color pv;
   i_img_dim x,y,t,ttx,tty,tt;
   int ch;
+  dIMCTXim(im);
 
-  mm_log((1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), "
+  im_log((aIMCTX, 1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), "
          "to(" i_DFp "), trans* %p)\n",
          im, src, i_DFcp(x1, y1), i_DFcp(x2, y2), i_DFcp(tx, ty), trans));
   
@@ -445,9 +466,10 @@ Returns: i_img *
 i_img *
 i_copy(i_img *src) {
   i_img_dim y, y1, x1;
+  dIMCTXim(src);
   i_img *im = i_sametype(src, src->xsize, src->ysize);
 
-  mm_log((1,"i_copy(src %p)\n", src));
+  im_log((aIMCTX,1,"i_copy(src %p)\n", src));
 
   if (!im)
     return NULL;
@@ -533,9 +555,10 @@ i_scaleaxis(i_img *im, double Value, int Axis) {
   i_img *new_img;
   int has_alpha = i_img_has_alpha(im);
   int color_chans = i_img_color_channels(im);
+  dIMCTXim(im);
 
   i_clear_error();
-  mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
+  im_log((aIMCTX, 1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
 
   if (Axis == XAXIS) {
     hsize = (i_img_dim)(0.5 + im->xsize * Value);
@@ -560,7 +583,7 @@ i_scaleaxis(i_img *im, double Value, int Axis) {
     iEnd = hsize;
   }
   
-  new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels);
+  new_img = i_img_8_new(hsize, vsize, im->channels);
   if (!new_img) {
     i_push_error(0, "cannot create output image");
     return NULL;
@@ -710,7 +733,7 @@ i_scaleaxis(i_img *im, double Value, int Axis) {
   myfree(l0);
   myfree(l1);
 
-  mm_log((1,"(%p) <- i_scaleaxis\n", new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_scaleaxis\n", new_img));
 
   return new_img;
 }
@@ -733,8 +756,9 @@ i_scale_nn(i_img *im, double scx, double scy) {
   i_img_dim nxsize,nysize,nx,ny;
   i_img *new_img;
   i_color val;
+  dIMCTXim(im);
 
-  mm_log((1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy));
+  im_log((aIMCTX, 1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy));
 
   nxsize = (i_img_dim) ((double) im->xsize * scx);
   if (nxsize < 1) {
@@ -755,7 +779,7 @@ i_scale_nn(i_img *im, double scx, double scy) {
     i_ppix(new_img,nx,ny,&val);
   }
 
-  mm_log((1,"(%p) <- i_scale_nn\n",new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_scale_nn\n",new_img));
 
   return new_img;
 }
@@ -773,7 +797,10 @@ For paletted images the palette is copied from the source.
 =cut
 */
 
-i_img *i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) {
+i_img *
+i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) {
+  dIMCTXim(src);
+
   if (src->type == i_direct_type) {
     if (src->bits == 8) {
       return i_img_empty_ch(NULL, xsize, ysize, src->channels);
@@ -816,7 +843,10 @@ For paletted images the equivalent direct type is returned.
 =cut
 */
 
-i_img *i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) {
+i_img *
+i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) {
+  dIMCTXim(src);
+
   if (src->bits == 8) {
     return i_img_empty_ch(NULL, xsize, ysize, channels);
   }
@@ -857,8 +887,9 @@ i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int par
   i_img_dim nxsize,nysize,nx,ny;
   i_img *new_img;
   i_color val;
+  dIMCTXim(im);
   
-  mm_log((1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
+  im_log((aIMCTX, 1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
 
   nxsize = im->xsize;
   nysize = im->ysize ;
@@ -880,7 +911,7 @@ i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int par
     i_ppix(new_img,nx,ny,&val);
   }
 
-  mm_log((1,"(%p) <- i_transform\n",new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_transform\n",new_img));
   return new_img;
 }
 
@@ -903,14 +934,15 @@ i_img_diff(i_img *im1,i_img *im2) {
   int ch, chb;
   float tdiff;
   i_color val1,val2;
+  dIMCTXim(im1);
 
-  mm_log((1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2));
+  im_log((aIMCTX, 1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2));
 
   xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
   yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
   chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
 
-  mm_log((1,"i_img_diff: b=(" i_DFp ") chb=%d\n",
+  im_log((aIMCTX, 1,"i_img_diff: b=(" i_DFp ") chb=%d\n",
          i_DFcp(xb,yb), chb));
 
   tdiff=0;
@@ -920,7 +952,7 @@ i_img_diff(i_img *im1,i_img *im2) {
 
     for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]);
   }
-  mm_log((1,"i_img_diff <- (%.2f)\n",tdiff));
+  im_log((aIMCTX, 1,"i_img_diff <- (%.2f)\n",tdiff));
   return tdiff;
 }
 
@@ -945,14 +977,15 @@ i_img_diffd(i_img *im1,i_img *im2) {
   int ch, chb;
   double tdiff;
   i_fcolor val1,val2;
+  dIMCTXim(im1);
 
-  mm_log((1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2));
+  im_log((aIMCTX, 1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2));
 
   xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
   yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
   chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
 
-  mm_log((1,"i_img_diffd: b(" i_DFp ") chb=%d\n",
+  im_log((aIMCTX, 1,"i_img_diffd: b(" i_DFp ") chb=%d\n",
          i_DFcp(xb, yb), chb));
 
   tdiff=0;
@@ -965,7 +998,7 @@ i_img_diffd(i_img *im1,i_img *im2) {
       tdiff += sdiff * sdiff;
     }
   }
-  mm_log((1,"i_img_diffd <- (%.2f)\n",tdiff));
+  im_log((aIMCTX, 1,"i_img_diffd <- (%.2f)\n",tdiff));
 
   return tdiff;
 }
@@ -975,17 +1008,18 @@ i_img_samef(i_img *im1,i_img *im2, double epsilon, char const *what) {
   i_img_dim x,y,xb,yb;
   int ch, chb;
   i_fcolor val1,val2;
+  dIMCTXim(im1);
 
   if (what == NULL)
     what = "(null)";
 
-  mm_log((1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what));
+  im_log((aIMCTX,1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what));
 
   xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
   yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
   chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
 
-  mm_log((1,"i_img_samef: b(" i_DFp ") chb=%d\n",
+  im_log((aIMCTX, 1,"i_img_samef: b(" i_DFp ") chb=%d\n",
          i_DFcp(xb, yb), chb));
 
   for(y = 0; y < yb; y++) {
@@ -996,14 +1030,14 @@ i_img_samef(i_img *im1,i_img *im2, double epsilon, char const *what) {
       for(ch = 0; ch < chb; ch++) {
        double sdiff = val1.channel[ch] - val2.channel[ch];
        if (fabs(sdiff) > epsilon) {
-         mm_log((1,"i_img_samef <- different %g @(" i_DFp ")\n",
+         im_log((aIMCTX, 1,"i_img_samef <- different %g @(" i_DFp ")\n",
                  sdiff, i_DFcp(x, y)));
          return 0;
        }
       }
     }
   }
-  mm_log((1,"i_img_samef <- same\n"));
+  im_log((aIMCTX, 1,"i_img_samef <- same\n"));
 
   return 1;
 }
@@ -1018,6 +1052,7 @@ i_haar(i_img *im) {
   int ch,c;
   i_img *new_img,*new_img2;
   i_color val1,val2,dval1,dval2;
+  dIMCTXim(im);
   
   mx=im->xsize;
   my=im->ysize;
@@ -1429,6 +1464,8 @@ int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
 i_img_dim
 i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
                const int *chans, int chan_count, int bits) {
+  dIMCTXim(im);
+
   if (bits < 1 || bits > 32) {
     i_push_error(0, "Invalid bits, must be 1..32");
     return -1;
@@ -1453,7 +1490,7 @@ i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samp
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
       }
index aba1399..087a0e9 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -2,12 +2,11 @@
 #define _IMAGE_H_
 
 #include "imconfig.h"
+#include "immacros.h"
 #include "imio.h"
 #include "iolayer.h"
-#include "log.h"
 #include "stackmach.h"
 
-
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif
@@ -48,22 +47,20 @@ extern void i_hsv_to_rgbf(i_fcolor *color);
 extern void i_rgb_to_hsv(i_color *color);
 extern void i_hsv_to_rgb(i_color *color);
 
-i_img *i_img_8_new(i_img_dim x,i_img_dim y,int ch);
-i_img *i_img_new( void );
-i_img *i_img_empty(i_img *im,i_img_dim x,i_img_dim y);
-i_img *i_img_empty_ch(i_img *im,i_img_dim x,i_img_dim y,int ch);
+i_img *im_img_8_new(pIMCTX, i_img_dim x,i_img_dim y,int ch);
+#define i_img_empty(im, x, y) i_img_empty_ch((im), (x), (y), 3)
+i_img *im_img_empty_ch(pIMCTX, i_img *im,i_img_dim x,i_img_dim y,int ch);
+#define i_img_empty_ch(im, x, y, ch) im_img_empty_ch(aIMCTX, (im), (x), (y), (ch))
 void   i_img_exorcise(i_img *im);
 void   i_img_destroy(i_img *im);
-i_img *i_img_alloc(void);
-void i_img_init(i_img *im);
+i_img *im_img_alloc(pIMCTX);
+void im_img_init(pIMCTX, i_img *im);
 
 void   i_img_info(i_img *im,i_img_dim *info);
 
 extern i_img *i_sametype(i_img *im, i_img_dim xsize, i_img_dim ysize);
 extern i_img *i_sametype_chans(i_img *im, i_img_dim xsize, i_img_dim ysize, int channels);
 
-i_img *i_img_pal_new(i_img_dim x, i_img_dim y, int ch, int maxpal);
-
 /* Image feature settings */
 
 void   i_img_setmask    (i_img *im,int ch_mask);
@@ -74,74 +71,36 @@ i_img_dim i_img_get_height(i_img *im);
 
 /* Base functions */
 
-extern int i_ppix(i_img *im,i_img_dim x,i_img_dim y, const i_color *val);
-extern int i_gpix(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
-extern int i_ppixf(i_img *im,i_img_dim x,i_img_dim y, const i_fcolor *val);
-extern int i_gpixf(i_img *im,i_img_dim x,i_img_dim y,i_fcolor *val);
-
-#define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val)))
-#define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val)))
-#define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val)))
-#define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val)))
-
-extern i_img_dim i_plin(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_color *vals);
-extern i_img_dim i_glin(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_color *vals);
-extern i_img_dim i_plinf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *vals);
-extern i_img_dim i_glinf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *vals);
-extern i_img_dim i_gsamp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samp, 
-                   const int *chans, int chan_count);
-extern i_img_dim i_gsampf(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, 
-                   const int *chans, int chan_count);
-extern i_img_dim i_gpal(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, i_palidx *vals);
-extern i_img_dim i_ppal(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, const i_palidx *vals);
-extern int i_addcolors(i_img *im, const i_color *colors, int count);
-extern int i_getcolors(i_img *im, int i, i_color *, int count);
-extern int i_colorcount(i_img *im);
-extern int i_maxcolors(i_img *im);
-extern int i_findcolor(i_img *im, const i_color *color, i_palidx *entry);
-extern int i_setcolors(i_img *im, int index, const i_color *colors, 
+extern int (i_ppix)(i_img *im,i_img_dim x,i_img_dim y, const i_color *val);
+extern int (i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
+extern int (i_ppixf)(i_img *im,i_img_dim x,i_img_dim y, const i_fcolor *val);
+extern int (i_gpixf)(i_img *im,i_img_dim x,i_img_dim y,i_fcolor *val);
+
+extern i_img_dim (i_plin)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
+                         const i_color *vals);
+extern i_img_dim (i_glin)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
+                         i_color *vals);
+extern i_img_dim (i_plinf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
+                          const i_fcolor *vals);
+extern i_img_dim (i_glinf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, 
+                          i_fcolor *vals);
+extern i_img_dim (i_gsamp)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
+                          i_sample_t *samp, const int *chans, int chan_count);
+extern i_img_dim
+(i_gsampf)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp,
+          const int *chans, int chan_count);
+extern i_img_dim
+(i_gpal)(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, i_palidx *vals);
+extern i_img_dim
+(i_ppal)(i_img *im, i_img_dim x, i_img_dim r, i_img_dim y, const i_palidx *vals);
+extern int (i_addcolors)(i_img *im, const i_color *colors, int count);
+extern int (i_getcolors)(i_img *im, int i, i_color *, int count);
+extern int (i_colorcount)(i_img *im);
+extern int (i_maxcolors)(i_img *im);
+extern int (i_findcolor)(i_img *im, const i_color *color, i_palidx *entry);
+extern int (i_setcolors)(i_img *im, int index, const i_color *colors, 
                        int count);
 
-#define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val))
-#define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val))
-#define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val))
-#define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val))
-
-#define i_gsamp(im, l, r, y, samps, chans, count) \
-  (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count)))
-#define i_gsampf(im, l, r, y, samps, chans, count) \
-  (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count)))
-
-#define i_gsamp_bits(im, l, r, y, samps, chans, count, bits) \
-  (((im)->i_f_gsamp_bits) ? ((im)->i_f_gsamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1)
-#define i_psamp_bits(im, l, r, y, samps, chans, count, bits) \
-  (((im)->i_f_psamp_bits) ? ((im)->i_f_psamp_bits)((im), (l), (r), (y), (samps), (chans), (count), (bits)) : -1)
-
-#define i_findcolor(im, color, entry) \
-  (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
-
-#define i_gpal(im, l, r, y, vals) \
-  (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0)
-#define i_ppal(im, l, r, y, vals) \
-  (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0)
-#define i_addcolors(im, colors, count) \
-  (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1)
-#define i_getcolors(im, index, color, count) \
-  (((im)->i_f_getcolors) ? \
-   ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0)
-#define i_setcolors(im, index, color, count) \
-  (((im)->i_f_setcolors) ? \
-   ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0)
-#define i_colorcount(im) \
-  (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1)
-#define i_maxcolors(im) \
-  (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1)
-#define i_findcolor(im, color, entry) \
-  (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
-
-#define i_img_virtual(im) ((im)->virtual)
-#define i_img_type(im) ((im)->type)
-#define i_img_bits(im) ((im)->bits)
 
 extern i_fill_t *i_new_fill_solidf(const i_fcolor *c, int combine);
 extern i_fill_t *i_new_fill_solid(const i_color *c, int combine);
@@ -225,10 +184,10 @@ int i_img_samef(i_img *im1,i_img *im2, double epsilon, const char *what);
 
 /* font routines */
 
-undef_int i_init_fonts( int t1log );
-
 #ifdef HAVE_LIBTT
 
+extern void i_tt_start(void);
+
 TT_Fonthandle* i_tt_new(const char *fontname);
 void i_tt_destroy( TT_Fonthandle *handle );
 undef_int i_tt_cp( TT_Fonthandle *handle,i_img *im,i_img_dim xb,i_img_dim yb,int channel,double points,char const* txt,size_t len,int smooth, int utf8, int align);
@@ -247,14 +206,15 @@ extern void i_quant_makemap(i_quantize *quant, i_img **imgs, int count);
 extern i_palidx *i_quant_translate(i_quantize *quant, i_img *img);
 extern void i_quant_transparent(i_quantize *quant, i_palidx *indices, i_img *img, i_palidx trans_index);
 
-extern i_img *i_img_pal_new(i_img_dim x, i_img_dim y, int channels, int maxpal);
+i_img *im_img_pal_new(pIMCTX, i_img_dim x, i_img_dim y, int ch, int maxpal);
+
 extern i_img *i_img_to_pal(i_img *src, i_quantize *quant);
 extern i_img *i_img_to_rgb(i_img *src);
 extern i_img *i_img_masked_new(i_img *targ, i_img *mask, i_img_dim x, i_img_dim y, 
                                i_img_dim w, i_img_dim h);
-extern i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch);
+extern i_img *im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch);
 extern i_img *i_img_to_rgb16(i_img *im);
-extern i_img *i_img_double_new(i_img_dim x, i_img_dim y, int ch);
+extern i_img *im_img_double_new(pIMCTX, i_img_dim x, i_img_dim y, int ch);
 extern i_img *i_img_to_drgb(i_img *im);
 
 extern int i_img_is_monochrome(i_img *im, int *zero_is_white);
@@ -331,36 +291,6 @@ i_new_fill_fount(double xa, double ya, double xb, double yb,
 
 void malloc_state( void );
 
-/* this is sort of obsolete now */
-
-typedef struct {
-  undef_int (*i_has_format)(char *frmt);
-  i_color*(*ICL_set)(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a);
-  void (*ICL_info)(const i_color *cl);
-
-  i_img*(*i_img_new)( void );
-  i_img*(*i_img_empty)(i_img *im,i_img_dim x,i_img_dim y);
-  i_img*(*i_img_empty_ch)(i_img *im,i_img_dim x,i_img_dim y,int ch);
-  void(*i_img_exorcise)(i_img *im);
-
-  void(*i_img_info)(i_img *im,i_img_dim *info);
-  
-  void(*i_img_setmask)(i_img *im,int ch_mask);
-  int (*i_img_getmask)(i_img *im);
-  
-  /*
-  int (*i_ppix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
-  int (*i_gpix)(i_img *im,i_img_dim x,i_img_dim y,i_color *val);
-  */
-  void(*i_box)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val);
-  void(*i_line)(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val,int endp);
-  void(*i_arc)(i_img *im,i_img_dim x,i_img_dim y,double rad,double d1,double d2,const i_color *val);
-  void(*i_copyto)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty);
-  void(*i_copyto_trans)(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans);
-  int(*i_rubthru)(i_img *im,i_img *src,i_img_dim tx,i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy);
-
-} symbol_table_t;
-
 #include "imerror.h"
 
 /* image tag processing */
@@ -398,11 +328,11 @@ extern void i_tags_print(i_img_tags *tags);
 
 /* image file limits */
 extern int
-i_set_image_file_limits(i_img_dim width, i_img_dim height, size_t bytes);
+im_set_image_file_limits(im_context_t ctx, i_img_dim width, i_img_dim height, size_t bytes);
 extern int
-i_get_image_file_limits(i_img_dim *width, i_img_dim *height, size_t *bytes);
+im_get_image_file_limits(im_context_t ctx, i_img_dim *width, i_img_dim *height, size_t *bytes);
 extern int
-i_int_check_image_file_limits(i_img_dim width, i_img_dim height, int channels, size_t sample_size);
+im_int_check_image_file_limits(im_context_t ctx, i_img_dim width, i_img_dim height, int channels, size_t sample_size);
 
 /* memory allocation */
 void* mymalloc(size_t size);
@@ -428,7 +358,6 @@ void  malloc_state(void);
 #endif /* IMAGER_MALLOC_DEBUG */
 
 #include "imrender.h"
-#include "immacros.h"
 
 extern void
 i_adapt_colors(int dest_channels, int src_channels, i_color *colors, 
@@ -452,6 +381,23 @@ extern int
 i_gsampf_bg(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samples, 
           int out_channels, i_fcolor const *bg);
 
+/* context object management */
+extern im_context_t im_context_new(void);
+extern void im_context_refinc(im_context_t ctx, const char *where);
+extern void im_context_refdec(im_context_t ctx, const char *where);
+extern im_context_t im_context_clone(im_context_t ctx, const char *where);
+extern im_slot_t im_context_slot_new(im_slot_destroy_t);
+extern void *im_context_slot_get(im_context_t ctx, im_slot_t slot);
+extern int im_context_slot_set(im_context_t ctx, im_slot_t slot, void *);
+
+extern im_context_t (*im_get_context)(void);
+
+/* mutex API */
+extern i_mutex_t i_mutex_new(void);
+extern void i_mutex_destroy(i_mutex_t m);
+extern void i_mutex_lock(i_mutex_t m);
+extern void i_mutex_unlock(i_mutex_t m);
+
 #include "imio.h"
 
 #endif
index 347e2ab..2848bcf 100644 (file)
--- a/imageri.h
+++ b/imageri.h
@@ -6,6 +6,7 @@
 #define IMAGEI_H_
 
 #include "imager.h"
+#include <stddef.h>
 
 /* wrapper functions that implement the floating point sample version of a 
    function in terms of the 8-bit sample version
@@ -111,4 +112,35 @@ i_img_dim i_abs(i_img_dim x);
 
 #define color_to_grey(col) ((col)->rgb.r * 0.222  + (col)->rgb.g * 0.707 + (col)->rgb.b * 0.071)
 
+#define IM_ERROR_COUNT 20
+typedef struct im_context_tag {
+  int error_sp;
+  size_t error_alloc[IM_ERROR_COUNT];
+  i_errmsg error_stack[IM_ERROR_COUNT];
+#ifdef IMAGER_LOG
+  /* the log file and level for this context */
+  FILE *lg_file;
+  int log_level;
+
+  /* whether we own the lg_file, false for stderr and for cloned contexts */
+  int own_log;
+
+  /* values supplied by lhead */
+  const char *filename;
+  int line;
+#endif
+
+  /* file size limits */
+  i_img_dim max_width, max_height;
+  size_t max_bytes;
+
+  /* per context storage */
+  size_t slot_alloc;
+  void **slots;
+
+  ptrdiff_t refcount;
+} im_context_struct;
+
+#define DEF_BYTES_LIMIT 0x40000000
+
 #endif
index b311a24..6a05efb 100644 (file)
@@ -7,7 +7,11 @@ use Getopt::Long;
 
 my @tests;
 my $verbose;
+my $nodc;
+my $make_opts = "";
 GetOptions("t|test=s" => \@tests,
+          "m=s" => \$make_opts,
+          "n" => \$nodc,
           "v" => \$verbose)
   or die;
 
@@ -18,18 +22,20 @@ if (-f 'Makefile') {
   run("$make clean");
 }
 run("cover -delete");
-run("perl Makefile.PL --coverage")
-  and die;
-run("$make 'OTHERLDFLAGS=-ftest-coverage -fprofile-arcs'")
-  and die;
+run("perl Makefile.PL --coverage @ARGV")
+  and die "Makefile.PL failed\n";
+run("$make $make_opts 'OTHERLDFLAGS=-ftest-coverage -fprofile-arcs'")
+  and die "build failed\n";
 
 {
   local $ENV{DEVEL_COVER_OPTIONS} = "-db," . getcwd() . "/cover_db,-coverage,statement,branch,condition,subroutine";
-  my $makecmd = "$make test TEST_VERBOSE=1 HARNESS_PERL_SWITCHES=-MDevel::Cover";
+  my $makecmd = "$make test TEST_VERBOSE=1";
+  $makecmd .= " HARNESS_PERL_SWITCHES=-MDevel::Cover" unless $nodc;
   if (@tests) {
     $makecmd .= " TEST_FILES='@tests'";
   }
-  run($makecmd);
+  run($makecmd)
+    and die "Test failed\n";
 }
 
 # build gcov files
@@ -38,6 +44,8 @@ my $mani = maniread();
 my %paths;
 for my $filename (keys %$mani) {
   next unless $filename =~ /\.(xs|c|im)$/;
+  (my $gcda = $filename) =~ s/\.\w+$/.gcda/;
+  next unless -f $gcda;
   if ($filename =~ m!^(\w+)/(\w+\.\w+)$!) {
     push @{$paths{$1}}, $2;
   }
index d9ad0eb..a7419ea 100644 (file)
@@ -6,6 +6,11 @@
 
 #define MAXCHANNELS 4
 
+typedef struct im_context_tag *im_context_t;
+
+typedef ptrdiff_t im_slot_t;
+typedef void (*im_slot_destroy_t)(void *);
+
 /* used for palette indices in some internal code (which might be 
    exposed at some point
 */
@@ -283,6 +288,14 @@ i_f_psamp - implements psamp() for this image.
 
 i_f_psampf - implements psamp() for this image.
 
+=item *
+
+C<im_data> - image specific data internal to Imager.
+
+=item *
+
+C<context> - the Imager API context this image belongs to.
+
 =back
 
 =cut
@@ -335,6 +348,9 @@ struct i_img_ {
   i_f_psampf_t i_f_psampf;
 
   void *im_data;
+
+  /* 0.91 */
+  im_context_t context;
 };
 
 /* ext_data for paletted images
@@ -540,6 +556,18 @@ typedef enum {
   ic_color
 } i_combine_t;
 
+/*
+=item i_mutex_t
+X<i_mutex>
+=category mutex
+=synopsis i_mutex_t mutex;
+
+Opaque type for Imager's mutex API.
+
+=cut
+ */
+typedef struct i_mutex_tag *i_mutex_t;
+
 /*
    describes an axis of a MM font.
    Modelled on FT2's FT_MM_Axis.
@@ -681,6 +709,13 @@ enum {
 
 #include "iolayert.h"
 
+/* error message information returned by im_errors() */
+
+typedef struct {
+  char *msg;
+  int code;
+} i_errmsg;
+
 typedef struct i_render_tag i_render;
 
 #ifdef IMAGER_FORMAT_ATTR
index da56df1..e1cfc94 100644 (file)
--- a/imerror.h
+++ b/imerror.h
@@ -5,23 +5,21 @@
    see error.c for documentation
    the error information is currently global
 */
-typedef struct {
-  char *msg;
-  int code;
-} i_errmsg;
-
 typedef void (*i_error_cb)(int code, char const *msg);
 typedef void (*i_failed_cb)(i_errmsg *msgs);
 extern i_error_cb i_set_error_cb(i_error_cb);
 extern i_failed_cb i_set_failed_cb(i_failed_cb);
 extern void i_set_argv0(char const *);
 extern int i_set_errors_fatal(int new_fatal);
-extern i_errmsg *i_errors(void);
+extern i_errmsg *im_errors(pIMCTX);
 
-extern void i_push_error(int code, char const *msg);
+extern void im_push_error(pIMCTX, int code, char const *msg);
+#ifndef IMAGER_NO_CONTEXT
 extern void i_push_errorf(int code, char const *fmt, ...) I_FORMAT_ATTR(2, 3);
-extern void i_push_errorvf(int code, char const *fmt, va_list);
-extern void i_clear_error(void);
+#endif
+extern void im_push_errorf(pIMCTX, int code, char const *fmt, ...) I_FORMAT_ATTR(3, 4);
+extern void im_push_errorvf(im_context_t ctx, int code, char const *fmt, va_list);
+extern void im_clear_error(pIMCTX);
 extern int i_failed(int code, char const *msg);
 
 #endif
diff --git a/imext.c b/imext.c
index 6afaadd..4471afe 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -2,6 +2,28 @@
 #include "imager.h"
 #include "imio.h"
 
+static im_context_t get_context(void);
+static i_img *mathom_i_img_8_new(i_img_dim, i_img_dim, int);
+static i_img *mathom_i_img_16_new(i_img_dim, i_img_dim, int);
+static i_img *mathom_i_img_double_new(i_img_dim, i_img_dim, int);
+static i_img *mathom_i_img_pal_new(i_img_dim, i_img_dim, int, int);
+static void mathom_i_clear_error(void);
+static void mathom_i_push_error(int, const char *);
+static void mathom_i_push_errorvf(int, const char *, va_list);
+static int mathom_i_set_image_file_limits(i_img_dim, i_img_dim, size_t);
+static int mathom_i_get_image_file_limits(i_img_dim*, i_img_dim*, size_t*);
+static int
+mathom_i_int_check_image_file_limits(i_img_dim, i_img_dim, int, size_t);
+static i_img *mathom_i_img_alloc(void);
+static void mathom_i_img_init(i_img *);
+static i_io_glue_t *mathom_io_new_fd(int);
+static i_io_glue_t *mathom_io_new_bufchain(void);
+static i_io_glue_t *
+mathom_io_new_buffer(const char *data, size_t, i_io_closebufp_t, void *);
+static i_io_glue_t *
+mathom_io_new_cb(void *, i_io_readl_t, i_io_writel_t, i_io_seekl_t,
+                i_io_closel_t, i_io_destroyl_t);
+
 /*
  DON'T ADD CASTS TO THESE
 */
@@ -18,10 +40,10 @@ im_ext_funcs imager_function_table =
     myfree_file_line,
     myrealloc_file_line,
 
-    i_img_8_new,
-    i_img_16_new,
-    i_img_double_new,
-    i_img_pal_new,
+    mathom_i_img_8_new,
+    mathom_i_img_16_new,
+    mathom_i_img_double_new,
+    mathom_i_img_pal_new,
     i_img_destroy,
     i_sametype,
     i_sametype_chans,
@@ -58,10 +80,10 @@ im_ext_funcs imager_function_table =
     i_quant_translate,
     i_quant_transparent,
 
-    i_clear_error,
-    i_push_error,
+    mathom_i_clear_error,
+    mathom_i_push_error,
     i_push_errorf,
-    i_push_errorvf,
+    mathom_i_push_errorvf,
 
     i_tags_new,
     i_tags_set,
@@ -99,9 +121,9 @@ im_ext_funcs imager_function_table =
     i_rubthru,
 
     /* IMAGER_API_LEVEL 2 functions */
-    i_set_image_file_limits,
-    i_get_image_file_limits,
-    i_int_check_image_file_limits,
+    mathom_i_set_image_file_limits,
+    mathom_i_get_image_file_limits,
+    mathom_i_int_check_image_file_limits,
 
     i_flood_fill_border,
     i_flood_cfill_border,
@@ -116,8 +138,8 @@ im_ext_funcs imager_function_table =
     i_loog,
 
     /* IMAGER_API_LEVEL 4 functions */
-    i_img_alloc,
-    i_img_init,
+    mathom_i_img_alloc,
+    mathom_i_img_init,
 
     /* IMAGER_API_LEVEL 5 functions */
     i_img_is_monochrome,
@@ -145,12 +167,44 @@ im_ext_funcs imager_function_table =
     i_io_close,
     i_io_set_buffered,
     i_io_gets,
-    io_new_fd,
-    io_new_bufchain,
-    io_new_buffer,
-    io_new_cb,
+    mathom_io_new_fd,
+    mathom_io_new_bufchain,
+    mathom_io_new_buffer,
+    mathom_io_new_cb,
     io_slurp,
-    io_glue_destroy
+    io_glue_destroy,
+
+    /* level 8 */
+    im_img_8_new,
+    im_img_16_new,
+    im_img_double_new,
+    im_img_pal_new,
+    im_clear_error,
+    im_push_error,
+    im_push_errorvf,
+    im_push_errorf,
+    im_set_image_file_limits,
+    im_get_image_file_limits,
+    im_int_check_image_file_limits,
+    im_img_alloc,
+    im_img_init,
+    im_io_new_fd,
+    im_io_new_bufchain,
+    im_io_new_buffer,
+    im_io_new_cb,
+    get_context,
+    im_lhead,
+    im_loog,
+    im_context_refinc,
+    im_context_refdec,
+    im_errors,
+    i_mutex_new,
+    i_mutex_destroy,
+    i_mutex_lock,
+    i_mutex_unlock,
+    im_context_slot_new,
+    im_context_slot_set,
+    im_context_slot_get
   };
 
 /* in general these functions aren't called by Imager internally, but
@@ -550,3 +604,106 @@ int
   return i_setcolors(im, index, colors, count);
 }
 
+/*
+=item im_get_context()
+
+Retrieve the context object for the current thread.
+
+Inside Imager itself this is just a function pointer, which the
+F<Imager.xs> BOOT handler initializes for use within perl.  If you're
+taking the Imager code and embedding it elsewhere you need to
+initialize the C<im_get_context> pointer at some point.
+
+=cut
+*/
+
+static im_context_t
+get_context(void) {
+  return im_get_context();
+}
+
+static i_img *
+mathom_i_img_8_new(i_img_dim xsize, i_img_dim ysize, int channels) {
+  return i_img_8_new(xsize, ysize, channels);
+}
+
+static i_img *
+mathom_i_img_16_new(i_img_dim xsize, i_img_dim ysize, int channels) {
+  return i_img_16_new(xsize, ysize, channels);
+}
+
+static i_img *
+mathom_i_img_double_new(i_img_dim xsize, i_img_dim ysize, int channels) {
+  return i_img_double_new(xsize, ysize, channels);
+}
+
+static i_img *
+mathom_i_img_pal_new(i_img_dim xsize, i_img_dim ysize, int channels,
+                    int maxpal) {
+  return i_img_pal_new(xsize, ysize, channels, maxpal);
+}
+
+static void
+mathom_i_clear_error(void) {
+  i_clear_error();
+}
+
+static void
+mathom_i_push_error(int code, const char *msg) {
+  i_push_error(code, msg);
+}
+
+static void
+mathom_i_push_errorvf(int code, const char *fmt, va_list args) {
+  i_push_errorvf(code, fmt, args);
+}
+
+static int
+mathom_i_set_image_file_limits(i_img_dim max_width, i_img_dim max_height,
+                              size_t max_bytes) {
+  return i_set_image_file_limits(max_width, max_height, max_bytes);
+}
+
+static int
+mathom_i_get_image_file_limits(i_img_dim *pmax_width, i_img_dim *pmax_height,
+                              size_t *pmax_bytes) {
+  return i_get_image_file_limits(pmax_width, pmax_height, pmax_bytes);
+}
+
+static int
+mathom_i_int_check_image_file_limits(i_img_dim width, i_img_dim height,
+                                    int channels, size_t sample_size) {
+  return i_int_check_image_file_limits(width, height, channels, sample_size);
+}
+
+static i_img *
+mathom_i_img_alloc(void) {
+  return i_img_alloc();
+}
+
+static void
+mathom_i_img_init(i_img *im) {
+  i_img_init(im);
+}
+
+static i_io_glue_t *
+mathom_io_new_fd(int fd) {
+  return io_new_fd(fd);
+}
+static i_io_glue_t *
+mathom_io_new_bufchain(void) {
+  return io_new_bufchain();
+}
+
+static i_io_glue_t *
+mathom_io_new_buffer(const char *data, size_t size, i_io_closebufp_t closefp,
+                    void *close_data) {
+  return io_new_buffer(data, size, closefp, close_data);
+}
+
+static i_io_glue_t *
+mathom_io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb,
+                i_io_seekl_t seekcb, i_io_closel_t closecb,
+                i_io_destroyl_t destroycb) {
+  return io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb);
+}
diff --git a/imext.h b/imext.h
index 8c42b90..6e834b2 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -30,6 +30,10 @@ extern im_ext_funcs *imager_function_ext_table;
 /* just for use here */
 #define im_extt imager_function_ext_table
 
+#define im_get_context() ((im_extt->f_im_get_context)())
+#define im_context_refinc(ctx, where) ((im_extt->f_im_context_refinc)((ctx), (where)))
+#define im_context_refdec(ctx, where) ((im_extt->f_im_context_refdec)((ctx), (where)))
+
 #ifdef IMAGER_DEBUG_MALLOC
 
 #define mymalloc(size) ((im_extt->f_mymalloc_file_line)((size), __FILE__, __LINE__))
@@ -44,10 +48,10 @@ extern im_ext_funcs *imager_function_ext_table;
 
 #endif
 
-#define i_img_8_new(xsize, ysize, channels) ((im_extt->f_i_img_8_new)((xsize), (ysize), (channels)))
-#define i_img_16_new(xsize, ysize, channels) ((im_extt->f_i_img_16_new)((xsize), (ysize), (channels)))
-#define i_img_double_new(xsize, ysize, channels) ((im_extt->f_i_img_double_new)((xsize), (ysize), (channels)))
-#define i_img_pal_new(xsize, ysize, channels, maxpal) ((im_extt->f_i_img_pal_new)((xsize), (ysize), (channels), (maxpal)))
+#define im_img_8_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_8_new)((ctx), (xsize), (ysize), (channels)))
+#define im_img_16_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_16_new)((ctx), (xsize), (ysize), (channels)))
+#define im_img_double_new(ctx, xsize, ysize, channels) ((im_extt->f_im_img_double_new)((ctx), (xsize), (ysize), (channels)))
+#define im_img_pal_new(ctx, xsize, ysize, channels, maxpal) ((im_extt->f_im_img_pal_new)((ctx), (xsize), (ysize), (channels), (maxpal)))
 
 #define i_img_destroy(im) ((im_extt->f_i_img_destroy)(im))
 #define i_sametype(im, xsize, ysize) ((im_extt->f_i_sametype)((im), (xsize), (ysize)))
@@ -58,43 +62,8 @@ extern im_ext_funcs *imager_function_ext_table;
 #define IMAGER_DIRECT_IMAGE_CALLS 1
 #endif
 
-#if IMAGER_DIRECT_IMAGE_CALLS
-#define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val)))
-#define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val)))
-#define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val)))
-#define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val)))
-#define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val))
-#define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val))
-#define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val))
-#define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val))
+#if !IMAGER_DIRECT_IMAGE_CALLS
 
-#define i_gsamp(im, l, r, y, samps, chans, count) \
-  (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count)))
-#define i_gsampf(im, l, r, y, samps, chans, count) \
-  (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count)))
-
-#define i_findcolor(im, color, entry) \
-  (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
-
-#define i_gpal(im, l, r, y, vals) \
-  (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0)
-#define i_ppal(im, l, r, y, vals) \
-  (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0)
-#define i_addcolors(im, colors, count) \
-  (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1)
-#define i_getcolors(im, index, color, count) \
-  (((im)->i_f_getcolors) ? \
-   ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0)
-#define i_setcolors(im, index, color, count) \
-  (((im)->i_f_setcolors) ? \
-   ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0)
-#define i_colorcount(im) \
-  (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1)
-#define i_maxcolors(im) \
-  (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1)
-#define i_findcolor(im, color, entry) \
-  (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
-#else
 #define i_ppix(im, x, y, val) ((im_extt->f_i_ppix)((im), (x), (y), (val)))
 #define i_gpix(im, x, y, val) ((im_extt->f_i_gpix)((im), (x), (y), (val)))
 #define i_ppixf(im, x, y, val) ((im_extt->f_i_ppixf)((im), (x), (y), (val)))
@@ -134,11 +103,11 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_quant_transparent(quant, indices, img, trans_index) \
   ((im_extt->f_i_quant_transparent)((quant), (indices), (img), (trans_index)))
 
-#define i_clear_error() ((im_extt->f_i_clear_error)())
-#define i_push_error(code, msg) ((im_extt->f_i_push_error)((code), (msg)))
+#define im_clear_error(ctx) ((im_extt->f_im_clear_error)(ctx))
+#define im_push_error(ctx, code, msg) ((im_extt->f_im_push_error)((ctx), (code), (msg)))
 #define i_push_errorf (im_extt->f_i_push_errorf)
-#define i_push_errorvf(code, fmt, list) \
-  ((im_extt->f_i_push_errorvf)((code), (fmt), (list)))
+#define im_push_errorvf(ctx, code, fmt, list)          \
+  ((im_extt->f_im_push_errorvf)((ctx), (code), (fmt), (list)))
 
 #define i_tags_new(tags) ((im_extt->f_i_tags_new)(tags))
 #define i_tags_set(tags, name, data, size) \
@@ -194,12 +163,12 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_rubthru(im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy) \
   ((im_extt->f_i_rubthru)((im), (src), (tx), (ty), (src_minx), (src_miny), (src_maxx), (src_maxy)))
 
-#define i_set_image_file_limits(max_width, max_height, max_bytes) \
-  ((im_extt->f_i_set_image_file_limits)((max_width), (max_height), (max_bytes)))
-#define i_get_image_file_limits(pmax_width, pmax_height, pmax_bytes) \
-  ((im_extt->f_i_get_image_file_limits)((pmax_width), (pmax_height), (pmax_bytes)))
-#define i_int_check_image_file_limits(width, height, channels, sample_size) \
-  ((im_extt->f_i_int_check_image_file_limits)((width), (height), (channels), (sample_size)))
+#define im_set_image_file_limits(ctx, max_width, max_height, max_bytes)        \
+  ((im_extt->f_im_set_image_file_limits)((max_width), (max_height), (max_bytes)))
+#define im_get_image_file_limits(ctx, pmax_width, pmax_height, pmax_bytes) \
+  ((im_extt->f_im_get_image_file_limits)((ctx), (pmax_width), (pmax_height), (pmax_bytes)))
+#define im_int_check_image_file_limits(ctx, width, height, channels, sample_size) \
+  ((im_extt->f_im_int_check_image_file_limits)((ctx), (width), (height), (channels), (sample_size)))
 
 #define i_img_setmask(img, mask) ((im_extt->f_i_img_setmask)((img), (mask)))
 #define i_img_getmask(img) ((im_extt->f_i_img_getmask)(img))
@@ -208,9 +177,11 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_img_get_height(img) ((im_extt->f_i_img_get_height)(img))
 #define i_lhead(file, line) ((im_extt->f_i_lhead)((file), (line)))
 #define i_loog (im_extt->f_i_loog)
+#define im_lhead(ctx, file, line) ((im_extt->f_im_lhead)((ctx), (file), (line)))
+#define im_loog (im_extt->f_im_loog)
 
-#define i_img_alloc() ((im_extt->f_i_img_alloc)())
-#define i_img_init(img) ((im_extt->f_i_img_init)(img))
+#define im_img_alloc(ctx) ((im_extt->f_im_img_alloc)(ctx))
+#define im_img_init(ctx, img) ((im_extt->fm_i_img_init)((ctx), (img)))
 
 #define i_img_is_monochrome(img, zero_is_white) ((im_extt->f_i_img_is_monochrome)((img), (zero_is_white)))
 
@@ -247,20 +218,33 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_io_close (im_extt->f_i_io_close)
 #define i_io_set_buffered (im_extt->f_i_io_set_buffered)
 #define i_io_gets (im_extt->f_i_io_gets)
-#define io_new_fd(fd) ((im_extt->f_io_new_fd)(fd))
-#define io_new_bufchain() ((im_extt->f_io_new_bufchain)())
-#define io_new_buffer(data, len, closecb, closedata) \
-  ((im_extt->f_io_new_buffer)((data), (len), (closecb), (closedata)))
-#define io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb) \
-  ((im_extt->f_io_new_cb)((p), (readcb), (writecb), (seekcb), (closecb), (destroycb)))
+#define im_io_new_fd(ctx, fd) ((im_extt->f_im_io_new_fd)(ctx, fd))
+#define im_io_new_bufchain(ctx) ((im_extt->f_im_io_new_bufchain)(ctx))
+#define im_io_new_buffer(ctx, data, len, closecb, closedata)           \
+  ((im_extt->f_im_io_new_buffer)((ctx), (data), (len), (closecb), (closedata)))
+#define im_io_new_cb(ctx, p, readcb, writecb, seekcb, closecb, destroycb) \
+  ((im_extt->f_im_io_new_cb)((ctx), (p), (readcb), (writecb), (seekcb), (closecb), (destroycb)))
 #define io_slurp(ig, datap) ((im_extt->f_io_slurp)((ig), (datap)))
 #define io_glue_destroy(ig) ((im_extt->f_io_glue_destroy)(ig))
 
+#define i_mutex_new() ((im_extt->f_i_mutex_new)())
+#define i_mutex_destroy(m) ((im_extt->f_i_mutex_destroy)(m))
+#define i_mutex_lock(m) ((im_extt->f_i_mutex_lock)(m))
+#define i_mutex_unlock(m) ((im_extt->f_i_mutex_unlock)(m))
+
+#define im_context_slot_new(destructor) ((im_extt->f_im_context_slot_new)(destructor))
+#define im_context_slot_get(ctx, slot) ((im_extt->f_im_context_slot_get)((ctx), (slot)))
+#define im_context_slot_set(ctx, slot, value) ((im_extt->f_im_context_slot_set)((ctx), (slot), (value)))
+
+#define im_push_errorf (im_extt->f_im_push_errorf)
+
 #ifdef IMAGER_LOG
+#ifndef IMAGER_NO_CONTEXT
 #define mm_log(x) { i_lhead(__FILE__,__LINE__); i_loog x; } 
+#endif
+#define im_log(x) { im_lhead(aIMCTX, __FILE__,__LINE__); im_loog x; } 
 #else
 #define mm_log(x)
 #endif
 
-
 #endif
index 9fd1a0c..ae3bc00 100644 (file)
@@ -34,7 +34,7 @@
  will result in an increment of IMAGER_API_LEVEL.
 */
 
-#define IMAGER_API_LEVEL 7
+#define IMAGER_API_LEVEL 8
 
 typedef struct {
   int version;
@@ -48,10 +48,10 @@ typedef struct {
   void  (*f_myfree_file_line)(void *p, char*file, int line);
   void* (*f_myrealloc_file_line)(void *p, size_t newsize, char* file,int line);
 
-  i_img *(*f_i_img_8_new)(i_img_dim xsize, i_img_dim ysize, int channels);
-  i_img *(*f_i_img_16_new)(i_img_dim xsize, i_img_dim ysize, int channels);
-  i_img *(*f_i_img_double_new)(i_img_dim xsize, i_img_dim ysize, int channels);
-  i_img *(*f_i_img_pal_new)(i_img_dim xsize, i_img_dim ysize, int channels, int maxpal);
+  i_img *(*f_i_img_8_new)(i_img_dim xsize, i_img_dim ysize, int channels); /* SKIP */
+  i_img *(*f_i_img_16_new)(i_img_dim xsize, i_img_dim ysize, int channels);  /* SKIP */
+  i_img *(*f_i_img_double_new)(i_img_dim xsize, i_img_dim ysize, int channels); /* SKIP */
+  i_img *(*f_i_img_pal_new)(i_img_dim xsize, i_img_dim ysize, int channels, int maxpal); /* SKIP */
   void (*f_i_img_destroy)(i_img *im);
   i_img *(*f_i_sametype)(i_img *im, i_img_dim xsize, i_img_dim ysize);
   i_img *(*f_i_sametype_chans)(i_img *im, i_img_dim xsize, i_img_dim ysize, int channels);
@@ -102,10 +102,10 @@ typedef struct {
   void (*f_i_quant_transparent)(i_quantize *quant, i_palidx *indices, 
                                 i_img *img, i_palidx trans_index);
 
-  void (*f_i_clear_error)(void);
-  void (*f_i_push_error)(int code, char const *msg);
+  void (*f_i_clear_error)(void); /* SKIP */
+  void (*f_i_push_error)(int code, char const *msg); /* SKIP */
   void (*f_i_push_errorf)(int code, char const *fmt, ...);
-  void (*f_i_push_errorvf)(int code, char const *fmt, va_list);
+  void (*f_i_push_errorvf)(int code, char const *fmt, va_list); /* SKIP */
   
   void (*f_i_tags_new)(i_img_tags *tags);
   int (*f_i_tags_set)(i_img_tags *tags, char const *name, char const *data, 
@@ -152,9 +152,9 @@ typedef struct {
   int (*f_i_rubthru)(i_img *im, i_img *src, i_img_dim tx, i_img_dim ty, i_img_dim src_minx, i_img_dim src_miny, i_img_dim src_maxx, i_img_dim src_maxy);
 
   /* IMAGER_API_LEVEL 2 functions */
-  int (*f_i_set_image_file_limits)(i_img_dim width, i_img_dim height, size_t bytes);
-  int (*f_i_get_image_file_limits)(i_img_dim *width, i_img_dim *height, size_t *bytes);
-  int (*f_i_int_check_image_file_limits)(i_img_dim width, i_img_dim height, int channels, size_t sample_size);
+  int (*f_i_set_image_file_limits)(i_img_dim width, i_img_dim height, size_t bytes); /* SKIP */
+  int (*f_i_get_image_file_limits)(i_img_dim *width, i_img_dim *height, size_t *bytes); /* SKIP */
+  int (*f_i_int_check_image_file_limits)(i_img_dim width, i_img_dim height, int channels, size_t sample_size); /* SKIP */
   int (*f_i_flood_fill_border)(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *dcol, const i_color *border);
   int (*f_i_flood_cfill_border)(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill, const i_color *border);
 
@@ -168,8 +168,8 @@ typedef struct {
   void (*f_i_loog)(int level, const char *msg, ...);
 
   /* IMAGER_API_LEVEL 4 functions will be added here */
-  i_img *(*f_i_img_alloc)(void);
-  void (*f_i_img_init)(i_img *);
+  i_img *(*f_i_img_alloc)(void); /* SKIP */
+  void (*f_i_img_init)(i_img *); /* SKIP */
 
   /* IMAGER_API_LEVEL 5 functions will be added here */
   /* added i_psampf?_bits macros */
@@ -210,15 +210,50 @@ typedef struct {
   int (*f_i_io_set_buffered)(io_glue *ig, int buffered);
   ssize_t (*f_i_io_gets)(io_glue *ig, char *, size_t, int);
 
-  i_io_glue_t *(*f_io_new_fd)(int fd);
-  i_io_glue_t *(*f_io_new_bufchain)(void);
-  i_io_glue_t *(*f_io_new_buffer)(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata);
-  i_io_glue_t *(*f_io_new_cb)(void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb);
+  i_io_glue_t *(*f_io_new_fd)(int fd); /* SKIP */
+  i_io_glue_t *(*f_io_new_bufchain)(void); /* SKIP */
+  i_io_glue_t *(*f_io_new_buffer)(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata); /* SKIP */
+  i_io_glue_t *(*f_io_new_cb)(void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb); /* SKIP */
   size_t (*f_io_slurp)(i_io_glue_t *ig, unsigned char **c);
   void (*f_io_glue_destroy)(i_io_glue_t *ig);
 
   /* IMAGER_API_LEVEL 8 functions will be added here */
+  i_img *(*f_im_img_8_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
+  i_img *(*f_im_img_16_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
+  i_img *(*f_im_img_double_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels);
+  i_img *(*f_im_img_pal_new)(im_context_t ctx, i_img_dim xsize, i_img_dim ysize, int channels, int maxpal);
 
+  void (*f_im_clear_error)(im_context_t ctx);
+  void (*f_im_push_error)(im_context_t ctx, int code, char const *msg);
+  void (*f_im_push_errorvf)(im_context_t ctx, int code, char const *fmt, va_list);
+  void (*f_im_push_errorf)(im_context_t , int code, char const *fmt, ...);
+
+  int (*f_im_set_image_file_limits)(im_context_t ctx, i_img_dim width, i_img_dim height, size_t bytes);
+  int (*f_im_get_image_file_limits)(im_context_t ctx, i_img_dim *width, i_img_dim *height, size_t *bytes);
+  int (*f_im_int_check_image_file_limits)(im_context_t ctx, i_img_dim width, i_img_dim height, int channels, size_t sample_size);
+
+  i_img *(*f_im_img_alloc)(im_context_t ctx);
+  void (*f_im_img_init)(im_context_t ctx, i_img *);
+
+  i_io_glue_t *(*f_im_io_new_fd)(im_context_t ctx, int fd);
+  i_io_glue_t *(*f_im_io_new_bufchain)(im_context_t ctx);
+  i_io_glue_t *(*f_im_io_new_buffer)(im_context_t ctx, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata);
+  i_io_glue_t *(*f_im_io_new_cb)(im_context_t ctx, void *p, i_io_readl_t readcb, i_io_writel_t writecb, i_io_seekl_t seekcb, i_io_closel_t closecb, i_io_destroyl_t destroycb);
+
+  im_context_t (*f_im_get_context)(void);
+
+  void (*f_im_lhead)( im_context_t, const char *file, int line  );
+  void (*f_im_loog)(im_context_t, int level,const char *msg, ... ) I_FORMAT_ATTR(3,4);
+  void (*f_im_context_refinc)(im_context_t, const char *where);
+  void (*f_im_context_refdec)(im_context_t, const char *where);
+  i_errmsg *(*f_im_errors)(im_context_t);
+  i_mutex_t (*f_i_mutex_new)(void);
+  void (*f_i_mutex_destroy)(i_mutex_t m);
+  void (*f_i_mutex_lock)(i_mutex_t m);
+  void (*f_i_mutex_unlock)(i_mutex_t m);
+  im_slot_t (*f_im_context_slot_new)(im_slot_destroy_t);
+  int (*f_im_context_slot_set)(im_context_t, im_slot_t, void *);
+  void *(*f_im_context_slot_get)(im_context_t, im_slot_t);
 } im_ext_funcs;
 
 #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
diff --git a/img16.c b/img16.c
index 970a173..1b3e452 100644 (file)
--- a/img16.c
+++ b/img16.c
@@ -20,6 +20,8 @@ sample image type to work with.
 =cut
 */
 
+#define IMAGER_NO_CONTEXT
+
 #include "imager.h"
 #include "imageri.h"
 
@@ -148,36 +150,40 @@ typedef unsigned short i_sample16_t;
      ((((i_sample16_t *)(bytes))[offset]+127) / 257)
 
 /*
-=item i_img_16_new(x, y, ch)
-
+=item im_img_16_new(ctx, x, y, ch)
+X<im_img_16_new API>X<i_img_16_new API>
 =category Image creation/destruction
+=synopsis i_img *img = im_img_16_new(aIMCTX, width, height, channels);
 =synopsis i_img *img = i_img_16_new(width, height, channels);
 
 Create a new 16-bit/sample image.
 
 Returns the image on success, or NULL on failure.
 
+Also callable as C<i_img_16_new(x, y, ch)>
+
 =cut
 */
 
-i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch) {
+i_img *
+im_img_16_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) {
   i_img *im;
   size_t bytes, line_bytes;
 
-  mm_log((1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
+  im_log((aIMCTX, 1,"i_img_16_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
          i_DFc(x), i_DFc(y), ch));
 
   if (x < 1 || y < 1) {
-    i_push_error(0, "Image sizes must be positive");
+    im_push_error(aIMCTX, 0, "Image sizes must be positive");
     return NULL;
   }
   if (ch < 1 || ch > MAXCHANNELS) {
-    i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
+    im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS);
     return NULL;
   }
   bytes =  x * y * ch * 2;
   if (bytes / y / ch / 2 != x) {
-    i_push_errorf(0, "integer overflow calculating image allocation");
+    im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
     return NULL;
   }
   
@@ -186,11 +192,11 @@ i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch) {
      working with the image */
   line_bytes = sizeof(i_fcolor) * x;
   if (line_bytes / x != sizeof(i_fcolor)) {
-    i_push_error(0, "integer overflow calculating scanline allocation");
+    im_push_error(aIMCTX, 0, "integer overflow calculating scanline allocation");
     return NULL;
   }
 
-  im = i_img_alloc();
+  im = im_img_alloc(aIMCTX);
   *im = IIM_base_16bit_direct;
   i_tags_new(&im->tags);
   im->xsize = x;
@@ -201,7 +207,7 @@ i_img *i_img_16_new(i_img_dim x, i_img_dim y, int ch) {
   im->idata = mymalloc(im->bytes);
   memset(im->idata, 0, im->bytes);
 
-  i_img_init(im);
+  im_img_init(aIMCTX, im);
 
   return im;
 }
@@ -223,8 +229,9 @@ i_img_to_rgb16(i_img *im) {
   i_img *targ;
   i_fcolor *line;
   i_img_dim y;
+  dIMCTXim(im);
 
-  targ = i_img_16_new(im->xsize, im->ysize, im->channels);
+  targ = im_img_16_new(aIMCTX, im->xsize, im->ysize, im->channels);
   if (!targ)
     return NULL;
   line = mymalloc(sizeof(i_fcolor) * im->xsize);
@@ -435,7 +442,8 @@ static i_img_dim i_gsamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return 0;
         }
       }
@@ -449,7 +457,8 @@ static i_img_dim i_gsamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return 0;
       }
@@ -486,7 +495,8 @@ static i_img_dim i_gsampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return 0;
         }
       }
@@ -500,7 +510,8 @@ static i_img_dim i_gsampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return 0;
       }
@@ -542,7 +553,8 @@ i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *sam
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
       }
@@ -556,6 +568,7 @@ i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *sam
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
+       dIMCTXim(im);
        i_push_error(0, "Invalid channel count");
        return -1;
       }
@@ -571,6 +584,7 @@ i_gsamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *sam
     return count;
   }
   else {
+    dIMCTXim(im);
     i_push_error(0, "Image position outside of image");
     return -1;
   }
@@ -584,6 +598,7 @@ i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned cons
   i_img_dim off;
 
   if (bits != 16) {
+    dIMCTXim(im);
     i_push_error(0, "Invalid bits for 16-bit image");
     return -1;
   }
@@ -599,7 +614,8 @@ i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned cons
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
       }
@@ -615,6 +631,7 @@ i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned cons
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
+       dIMCTXim(im);
        i_push_error(0, "Invalid channel count");
        return -1;
       }
@@ -632,6 +649,7 @@ i_psamp_bits_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned cons
     return count;
   }
   else {
+    dIMCTXim(im);
     i_push_error(0, "Image position outside of image");
     return -1;
   }
@@ -671,7 +689,8 @@ i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
       int all_in_mask = 1;
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
        if (!((1 << chans[ch]) & im->ch_mask))
@@ -701,7 +720,8 @@ i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return -1;
       }
@@ -721,6 +741,7 @@ i_psamp_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     return count;
   }
   else {
+    dIMCTXim(im);
     i_push_error(0, "Image position outside of image");
     return -1;
   }
@@ -760,7 +781,8 @@ i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
       int all_in_mask = 1;
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
        if (!((1 << chans[ch]) & im->ch_mask))
@@ -793,7 +815,8 @@ i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return -1;
       }
@@ -815,6 +838,7 @@ i_psampf_d16(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     return count;
   }
   else {
+    dIMCTXim(im);
     i_push_error(0, "Image position outside of image");
     return -1;
   }
diff --git a/img8.c b/img8.c
index d905fa2..c780473 100644 (file)
--- a/img8.c
+++ b/img8.c
@@ -1,3 +1,5 @@
+#define IMAGER_NO_CONTEXT
+
 #include "imager.h"
 #include "imageri.h"
 
@@ -80,10 +82,10 @@ static i_img IIM_base_8bit_direct =
   }*/
 
 /*
-=item i_img_8_new(x, y, ch)
-
+=item im_img_8_new(ctx, x, y, ch)
+X<im_img_8_new API>X<i_img_8_new API>
 =category Image creation/destruction
-
+=synopsis i_img *img = im_img_8_new(aIMCTX, width, height, channels);
 =synopsis i_img *img = i_img_8_new(width, height, channels);
 
 Creates a new image object I<x> pixels wide, and I<y> pixels high with
@@ -92,49 +94,16 @@ I<ch> channels.
 =cut
 */
 
-
 i_img *
-i_img_8_new(i_img_dim x,i_img_dim y,int ch) {
+im_img_8_new(pIMCTX, i_img_dim x,i_img_dim y,int ch) {
   i_img *im;
 
-  mm_log((1,"IIM_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
+  im_log((aIMCTX, 1,"im_img_8_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
          i_DFc(x), i_DFc(y), ch));
 
-  im=i_img_empty_ch(NULL,x,y,ch);
-  
-  mm_log((1,"(%p) <- IIM_new\n",im));
-  return im;
-}
-
-/* 
-=item i_img_new()
-
-Create new image reference - notice that this isn't an object yet and
-this should be fixed asap.
-
-=cut
-*/
-
-
-i_img *
-i_img_new() {
-  i_img *im;
-  
-  mm_log((1,"i_img_struct()\n"));
-
-  im = i_img_alloc();
-  
-  *im = IIM_base_8bit_direct;
-  im->xsize=0;
-  im->ysize=0;
-  im->channels=3;
-  im->ch_mask=MAXINT;
-  im->bytes=0;
-  im->idata=NULL;
-
-  i_img_init(im);
+  im = im_img_empty_ch(aIMCTX, NULL,x,y,ch);
   
-  mm_log((1,"(%p) <- i_img_struct\n",im));
+  im_log((aIMCTX, 1,"(%p) <- IIM_new\n",im));
   return im;
 }
 
@@ -155,10 +124,10 @@ Should this just call i_img_empty_ch()?
 */
 
 i_img *
-i_img_empty(i_img *im,i_img_dim x,i_img_dim y) {
-  mm_log((1,"i_img_empty(*im %p, x %" i_DF ", y %" i_DF ")\n",
+im_img_empty(pIMCTX, i_img *im,i_img_dim x,i_img_dim y) {
+  im_log((aIMCTX, 1,"i_img_empty(*im %p, x %" i_DF ", y %" i_DF ")\n",
          im, i_DFc(x), i_DFc(y)));
-  return i_img_empty_ch(im, x, y, 3);
+  return im_img_empty_ch(aIMCTX, im, x, y, 3);
 }
 
 /* 
@@ -175,29 +144,29 @@ Re-new image reference
 */
 
 i_img *
-i_img_empty_ch(i_img *im,i_img_dim x,i_img_dim y,int ch) {
+im_img_empty_ch(pIMCTX, i_img *im,i_img_dim x,i_img_dim y,int ch) {
   size_t bytes;
 
-  mm_log((1,"i_img_empty_ch(*im %p, x %" i_DF ", y %" i_DF ", ch %d)\n",
+  im_log((aIMCTX, 1,"i_img_empty_ch(*im %p, x %" i_DF ", y %" i_DF ", ch %d)\n",
          im, i_DFc(x), i_DFc(y), ch));
 
   if (x < 1 || y < 1) {
-    i_push_error(0, "Image sizes must be positive");
+    im_push_error(aIMCTX, 0, "Image sizes must be positive");
     return NULL;
   }
   if (ch < 1 || ch > MAXCHANNELS) {
-    i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
+    im_push_errorf(aIMCTX, 0, "channels must be between 1 and %d", MAXCHANNELS);
     return NULL;
   }
   /* check this multiplication doesn't overflow */
   bytes = x*y*ch;
   if (bytes / y / ch != x) {
-    i_push_errorf(0, "integer overflow calculating image allocation");
+    im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
     return NULL;
   }
 
   if (im == NULL)
-    im = i_img_alloc();
+    im = im_img_alloc(aIMCTX);
 
   memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
   i_tags_new(&im->tags);
@@ -207,14 +176,14 @@ i_img_empty_ch(i_img *im,i_img_dim x,i_img_dim y,int ch) {
   im->ch_mask  = MAXINT;
   im->bytes=bytes;
   if ( (im->idata=mymalloc(im->bytes)) == NULL) 
-    i_fatal(2,"malloc() error\n"); 
+    im_fatal(aIMCTX, 2,"malloc() error\n"); 
   memset(im->idata,0,(size_t)im->bytes);
   
   im->ext_data = NULL;
 
-  i_img_init(im);
+  im_img_init(aIMCTX, im);
   
-  mm_log((1,"(%p) <- i_img_empty_ch\n",im));
+  im_log((aIMCTX, 1,"(%p) <- i_img_empty_ch\n",im));
   return im;
 }
 
@@ -503,7 +472,8 @@ i_gsamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps,
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return 0;
         }
       }
@@ -517,7 +487,8 @@ i_gsamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_sample_t *samps,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return 0;
       }
@@ -557,7 +528,8 @@ i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps,
   unsigned char *data;
   for (ch = 0; ch < chan_count; ++ch) {
     if (chans[ch] < 0 || chans[ch] >= im->channels) {
-      i_push_errorf(0, "No channel %d in this image", chans[ch]);
+      dIMCTXim(im);
+      im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
     }
   }
   if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
@@ -571,7 +543,8 @@ i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps,
       /* make sure we have good channel numbers */
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return 0;
         }
       }
@@ -585,7 +558,8 @@ i_gsampf_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samps,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+       dIMCTXim(im);
+       im_push_errorf(aIMCTX, 0, "chan_count %d out of range, must be >0, <= channels", 
                      chan_count);
        return 0;
       }
@@ -638,7 +612,8 @@ i_psamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
       int all_in_mask = 1;
       for (ch = 0; ch < chan_count; ++ch) {
         if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
+         dIMCTXim(im);
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
           return -1;
         }
        if (!((1 << chans[ch]) & im->ch_mask))
@@ -667,7 +642,8 @@ i_psamp_d(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y,
     }
     else {
       if (chan_count <= 0 || chan_count > im->channels) {
-       i_push_errorf(0,&