Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

improved thread safety for Imager

  • Loading branch information...
commit 02c63035876648ef730cbde539cd75c53236a032 2 parents 41c938e + 7d99bed
@tonycoz authored
Showing with 3,420 additions and 1,091 deletions.
  1. +15 −0 Changes
  2. +5 −0 FT2/Changes
  3. +50 −1 FT2/FT2.pm
  4. +1 −0  FT2/FT2.xs
  5. +1 −0  FT2/MANIFEST
  6. +1 −0  FT2/Makefile.PL
  7. +60 −14 FT2/freetyp2.c
  8. +1 −1  FT2/imft2.h
  9. +59 −0 FT2/t/t20thread.t
  10. +1 −1  GIF/GIF.pm
  11. +1 −0  GIF/GIF.xs
  12. +41 −4 GIF/imgif.c
  13. +1 −0  GIF/imgif.h
  14. +6 −9 Imager.pm
  15. +94 −1 Imager.xs
  16. +1 −1  JPEG/JPEG.pm
  17. +10 −46 JPEG/imjpeg.c
  18. +9 −1 MANIFEST
  19. +29 −4 Makefile.PL
  20. +9 −0 T1/Changes
  21. +1 −0  T1/MANIFEST
  22. +64 −27 T1/T1.pm
  23. +35 −27 T1/T1.xs
  24. +194 −50 T1/imt1.c
  25. +13 −11 T1/imt1.h
  26. +54 −27 T1/t/t10type1.t
  27. +1 −0  T1/typemap
  28. +6 −0 TIFF/Changes
  29. +1 −1  TIFF/TIFF.pm
  30. +1 −0  TIFF/TIFF.xs
  31. +204 −15 TIFF/imtiff.c
  32. +1 −0  TIFF/imtiff.h
  33. +3 −3 apidocs.perl
  34. BIN  bench/largish.png
  35. BIN  bench/largish.tif
  36. +46 −0 bench/tifthread.pl
  37. +39 −20 bmp.c
  38. +284 −0 context.c
  39. +5 −3 conv.im
  40. +7 −5 convert.im
  41. +4 −2 datatypes.c
  42. +79 −24 draw.c
  43. +8 −4 dynaload.c
  44. +68 −124 error.c
  45. +28 −1 ext.h
  46. +1 −0  fills.c
  47. +31 −19 filters.im
  48. +5 −4 flip.im
  49. +59 −33 font.c → fontft1.c
  50. +3 −1 gaussian.im
  51. +5 −2 hlines.c
  52. +83 −46 image.c
  53. +61 −115 imager.h
  54. +26 −0 imageri.h
  55. +35 −0 imdatatypes.h
  56. +7 −9 imerror.h
  57. +52 −17 imext.c
  58. +43 −59 imext.h
  59. +34 −18 imexttypes.h
  60. +45 −21 img16.c
  61. +41 −61 img8.c
  62. +34 −17 imgdouble.c
  63. +88 −0 immacros.h
  64. +102 −50 iolayer.c
  65. +4 −4 iolayer.h
  66. +2 −0  iolayert.h
  67. +219 −13 lib/Imager/API.pod
  68. +310 −65 lib/Imager/APIRef.pod
  69. +76 −0 lib/Imager/Threads.pod
  70. +43 −38 limits.c
  71. +82 −42 log.c
  72. +19 −2 log.h
  73. +14 −4 maskimg.c
  74. +39 −0 mutexnull.c
  75. +44 −0 mutexpthr.c
  76. +94 −0 mutexwin.c
  77. +32 −13 palimg.c
  78. +8 −8 plug.h
  79. +36 −1 t/t82inline.t
  80. +92 −0 t/t84inlinectx.t
  81. +2 −0  t/x20spell.t
  82. +6 −2 t/x90cmpversion.t
  83. +2 −0  typemap.local
View
15 Changes
@@ -1,5 +1,20 @@
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.
+
+ - T1:
+ - improve error reporting
+ - provide better control of the level of anti-aliasing
+
Imager 0.92 - 14 Aug 2012
===========
View
5 FT2/Changes
@@ -1,6 +1,11 @@
Imager-Font-FT2 0.85
====================
+ - improve thread safety
+
+Imager-Font-FT2 0.85
+====================
+
- no longer fallback to using DynaLoader to load the XS code
https://rt.cpan.org/Ticket/Display.html?id=75560
View
51 FT2/FT2.pm
@@ -1,11 +1,12 @@
package Imager::Font::FT2;
use strict;
use Imager;
+use Scalar::Util ();
use vars qw($VERSION @ISA);
@ISA = qw(Imager::Font);
BEGIN {
- $VERSION = "0.85";
+ $VERSION = "0.86";
require XSLoader;
XSLoader::load('Imager::Font::FT2', $VERSION);
@@ -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__
View
1  FT2/FT2.xs
@@ -356,3 +356,4 @@ i_ft2_set_mm_coords(handle, ...)
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
+ i_ft2_start();
View
1  FT2/MANIFEST
@@ -16,4 +16,5 @@ MANIFEST This list of files
MANIFEST.SKIP
README
t/t10ft2.t
+t/t20thread.t
typemap
View
1  FT2/Makefile.PL
@@ -61,6 +61,7 @@ else {
$opts{PREREQ_PM} =
{
@Imager_req,
+ 'Scalar::Util' => 1.00,
XSLoader => 0,
};
}
View
74 FT2/freetyp2.c
@@ -49,40 +49,84 @@ 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 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);
+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;
+}
+
+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;
+ }
- return 1;
+ 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;
@@ -138,14 +182,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");
@@ -173,6 +218,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;
@@ -244,7 +290,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");
View
2  FT2/imft2.h
@@ -7,7 +7,7 @@ typedef struct FT2_Fonthandle FT2_Fonthandle;
typedef FT2_Fonthandle* Imager__Font__FT2x;
-extern int i_ft2_init(void);
+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);
View
59 FT2/t/t20thread.t
@@ -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();
View
2  GIF/GIF.pm
@@ -4,7 +4,7 @@ use Imager;
use vars qw($VERSION @ISA);
BEGIN {
- $VERSION = "0.84";
+ $VERSION = "0.85";
require XSLoader;
XSLoader::load('Imager::File::GIF', $VERSION);
View
1  GIF/GIF.xs
@@ -147,3 +147,4 @@ i_readgif_multi_wiol(ig)
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
PERL_INITIALIZE_IMAGER_PERL_CALLBACKS;
+ i_init_gif();
View
45 GIF/imgif.c
@@ -69,6 +69,12 @@ static int
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
+static i_mutex_t mutex;
+
+void
+i_init_gif(void) {
+ mutex = i_mutex_new();
+}
static
void
@@ -846,17 +852,25 @@ static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
i_img **
i_readgif_multi_wiol(io_glue *ig, int *count) {
GifFileType *GifFile;
-
+ i_img **result;
+
+ i_mutex_lock(mutex);
+
i_clear_error();
if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
gif_push_error();
i_push_error(0, "Cannot create giflib callback object");
mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
+ i_mutex_unlock(mutex);
return NULL;
}
- return i_readgif_multi_low(GifFile, count, -1);
+ result = i_readgif_multi_low(GifFile, count, -1);
+
+ i_mutex_unlock(mutex);
+
+ return result;
}
static int
@@ -869,6 +883,9 @@ io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
i_img *
i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
GifFileType *GifFile;
+ i_img *result;
+
+ i_mutex_lock(mutex);
i_clear_error();
@@ -876,10 +893,15 @@ i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
gif_push_error();
i_push_error(0, "Cannot create giflib callback object");
mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+ i_mutex_unlock(mutex);
return NULL;
}
- return i_readgif_low(GifFile, color_table, colors);
+ result = i_readgif_low(GifFile, color_table, colors);
+
+ i_mutex_unlock(mutex);
+
+ return result;
}
/*
@@ -924,6 +946,7 @@ Returns NULL if the page isn't found.
i_img *
i_readgif_single_wiol(io_glue *ig, int page) {
GifFileType *GifFile;
+ i_img *result;
i_clear_error();
if (page < 0) {
@@ -931,14 +954,21 @@ i_readgif_single_wiol(io_glue *ig, int page) {
return NULL;
}
+ i_mutex_lock(mutex);
+
if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
gif_push_error();
i_push_error(0, "Cannot create giflib callback object");
mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
+ i_mutex_unlock(mutex);
return NULL;
}
- return i_readgif_single_low(GifFile, page);
+ result = i_readgif_single_low(GifFile, page);
+
+ i_mutex_unlock(mutex);
+
+ return result;
}
/*
@@ -1797,6 +1827,8 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
GifFileType *GifFile;
int result;
+ i_mutex_lock(mutex);
+
i_clear_error();
gif_set_version(quant, imgs, count);
@@ -1805,11 +1837,14 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
gif_push_error();
i_push_error(0, "Cannot create giflib callback object");
mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
+ i_mutex_unlock(mutex);
return 0;
}
result = i_writegif_low(quant, GifFile, imgs, count);
+ i_mutex_unlock(mutex);
+
if (i_io_close(ig))
return 0;
@@ -1945,6 +1980,8 @@ EGifSetGifVersion(). See L<gif_set_version> for an explanation.
Arnar M. Hrafnkelsson, addi@umich.edu
+Tony Cook <tonyc@cpan.org>
+
=head1 SEE ALSO
perl(1), Imager(3)
View
1  GIF/imgif.h
@@ -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);
View
15 Imager.pm
@@ -4363,6 +4363,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
@@ -4804,6 +4808,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>,
@@ -4817,15 +4823,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.
View
95 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
@@ -3989,6 +4051,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
View
2  JPEG/JPEG.pm
@@ -4,7 +4,7 @@ use Imager;
use vars qw($VERSION @ISA);
BEGIN {
- $VERSION = "0.84";
+ $VERSION = "0.85";
require XSLoader;
XSLoader::load('Imager::File::JPEG', $VERSION);
View
56 JPEG/imjpeg.c
@@ -28,12 +28,12 @@ Reads and writes JPEG images
#include <unistd.h>
#endif
#include <setjmp.h>
+#include <string.h>
#include "jpeglib.h"
#include "jerror.h"
#include <errno.h>
#include <stdlib.h>
-#include <stdio.h>
#include "imexif.h"
#define JPEG_APP13 0xED /* APP13 marker code */
@@ -44,15 +44,8 @@ Reads and writes JPEG images
static unsigned char fake_eoi[]={(JOCTET) 0xFF,(JOCTET) JPEG_EOI};
-/* Bad design right here */
-
-static int tlength=0;
-static char **iptc_text=NULL;
-
-
/* Source and Destination managers */
-
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
io_glue *data;
@@ -71,7 +64,6 @@ typedef struct {
typedef wiol_source_mgr *wiol_src_ptr;
typedef wiol_destination_mgr *wiol_dest_ptr;
-
/*
* Methods for io manager objects
*
@@ -271,38 +263,6 @@ jpeg_wiol_dest(j_compress_ptr cinfo, io_glue *ig) {
dest->pub.next_output_byte = dest->buffer;
}
-LOCAL(unsigned int)
-jpeg_getc (j_decompress_ptr cinfo)
-/* Read next byte */
-{
- struct jpeg_source_mgr * datasrc = cinfo->src;
-
- if (datasrc->bytes_in_buffer == 0) {
- if (! (*datasrc->fill_input_buffer) (cinfo))
- { fprintf(stderr,"Jpeglib: cant suspend.\n"); exit(3); }
- /* ERREXIT(cinfo, JERR_CANT_SUSPEND);*/
- }
- datasrc->bytes_in_buffer--;
- return GETJOCTET(*datasrc->next_input_byte++);
-}
-
-METHODDEF(boolean)
-APP13_handler (j_decompress_ptr cinfo) {
- INT32 length;
- unsigned int cnt=0;
-
- length = jpeg_getc(cinfo) << 8;
- length += jpeg_getc(cinfo);
- length -= 2; /* discount the length word itself */
-
- tlength=length;
-
- if ( ((*iptc_text)=mymalloc(length)) == NULL ) return FALSE;
- while (--length >= 0) (*iptc_text)[cnt++] = jpeg_getc(cinfo);
-
- return TRUE;
-}
-
METHODDEF(void)
my_output_message (j_common_ptr cinfo) {
char buffer[JMSG_LENGTH_MAX];
@@ -400,7 +360,9 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
i_clear_error();
- iptc_text = iptc_itext;
+ *iptc_itext = NULL;
+ *itlength = 0;
+
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = my_error_exit;
jerr.pub.output_message = my_output_message;
@@ -410,8 +372,6 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
if (src_set)
wiol_term_source(&cinfo);
jpeg_destroy_decompress(&cinfo);
- *iptc_itext=NULL;
- *itlength=0;
if (line_buffer)
myfree(line_buffer);
if (im)
@@ -420,7 +380,7 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
}
jpeg_create_decompress(&cinfo);
- jpeg_set_marker_processor(&cinfo, JPEG_APP13, APP13_handler);
+ jpeg_save_markers(&cinfo, JPEG_APP13, 0xFFFF);
jpeg_save_markers(&cinfo, JPEG_APP1, 0xFFFF);
jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
jpeg_wiol_src(&cinfo, data, length);
@@ -515,6 +475,11 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
else if (markerp->marker == JPEG_APP1 && !seen_exif) {
seen_exif = i_int_decode_exif(im, markerp->data, markerp->data_length);
}
+ else if (markerp->marker == JPEG_APP13) {
+ *iptc_itext = mymalloc(markerp->data_length);
+ memcpy(*iptc_itext, markerp->data, markerp->data_length);
+ *itlength = markerp->data_length;
+ }
markerp = markerp->next;
}
@@ -557,7 +522,6 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
- *itlength=tlength;
i_tags_set(&im->tags, "i_format", "jpeg", 4);
View
10 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,7 +50,7 @@ Flines/Flines.xs
Flines/Makefile.PL
Flines/t/t00flines.t
flip.im
-font.c
+fontft1.c
fontfiles/dodge.ttf
fontfiles/ExistenceTest.ttf generated using pfaedit
fontfiles/ImUgly.ttf
@@ -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
@@ -199,6 +201,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
@@ -215,6 +218,9 @@ MANIFEST
MANIFEST.SKIP
map.c
maskimg.c
+mutexnull.c
+mutexpthr.c
+mutexwin.c
palimg.c
paste.im
plug.h
@@ -349,6 +355,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
@@ -373,6 +380,7 @@ T1/t/t10type1.t
T1/t/t20oo.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
View
33 Makefile.PL
@@ -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,42 @@ 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";
+ }
+}
+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 +546,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
View
9 T1/Changes
@@ -1,3 +1,12 @@
+Imager::Font::T1 1.018
+======================
+
+ - use mutexes to avoid re-entrancy into the thread-unsafe T1Lib
+
+ - improve error handling and reporting
+
+ - provide better control of the level of anti-aliasing
+
Imager::Font::T1 1.017
======================
View
1  T1/MANIFEST
@@ -16,3 +16,4 @@ t/t10type1.t
t/t20oo.t
T1.pm
T1.xs
+typemap
View
91 T1/T1.pm
@@ -5,7 +5,7 @@ use vars qw(@ISA $VERSION);
@ISA = qw(Imager::Font);
BEGIN {
- $VERSION = "1.017";
+ $VERSION = "1.018";
require XSLoader;
XSLoader::load('Imager::Font::T1', $VERSION);
@@ -14,17 +14,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 +55,42 @@ 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;
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;
@@ -110,7 +103,7 @@ sub _bounding_box {
$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);
}
@@ -122,8 +115,8 @@ sub has_chars {
$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,7 +126,7 @@ sub utf8 {
sub face_name {
my ($self) = @_;
- i_t1_face_name($self->{id});
+ return $self->{t1font}->face_name();
}
sub glyph_names {
@@ -144,9 +137,27 @@ sub glyph_names {
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->{t1aa} = $new_t1aa;
+ }
+ else {
+ $t1aa = $new_t1aa;
+ }
+
+ return 1;
+}
1;
@@ -197,6 +208,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
View
62 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 {
@@ -197,3 +204,4 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
+ i_t1_start();
View
244 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;
+
+ mm_log((1, "i_t1_new() -> %p (%d)\n", font, font_id));
- return 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;
- if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
+ 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);
+ }
+
+ 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;
}
}
@@ -594,103 +732,109 @@ t1_push_error(void) {
#ifdef T1ERR_SCAN_FONT_FORMAT
case T1ERR_SCAN_FONT_FORMAT:
- i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
+ i_push_error(T1ERR_SCAN_FONT_FORMAT, "Attempt to Load Multiple Master Font");
break;
#endif
#ifdef T1ERR_SCAN_FILE_OPEN_ERR
case T1ERR_SCAN_FILE_OPEN_ERR:
- i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
+ i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "Type 1 Font File Open Error");
break;
#endif
#ifdef T1ERR_SCAN_OUT_OF_MEMORY
case T1ERR_SCAN_OUT_OF_MEMORY:
- i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
+ i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "Virtual Memory Exceeded");
break;
#endif
#ifdef T1ERR_SCAN_ERROR
case T1ERR_SCAN_ERROR:
- i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
+ i_push_error(T1ERR_SCAN_ERROR, "Syntactical Error Scanning Font File");
break;
#endif
#ifdef T1ERR_SCAN_FILE_EOF
case T1ERR_SCAN_FILE_EOF:
- i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
+ i_push_error(T1ERR_SCAN_FILE_EOF, "Premature End of Font File Encountered");
break;
#endif
#ifdef T1ERR_PATH_ERROR
case T1ERR_PATH_ERROR:
- i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
+ i_push_error(T1ERR_PATH_ERROR, "Path Construction Error");
break;
#endif
#ifdef T1ERR_PARSE_ERROR
case T1ERR_PARSE_ERROR:
- i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
+ i_push_error(T1ERR_PARSE_ERROR, "Font is Corrupt");
break;
#endif
#ifdef T1ERR_TYPE1_ABORT
case T1ERR_TYPE1_ABORT:
- i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
+ i_push_error(T1ERR_TYPE1_ABORT, "Rasterization Aborted");
break;
#endif
#ifdef T1ERR_INVALID_FONTID
case T1ERR_INVALID_FONTID:
- i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
+ i_push_error(T1ERR_INVALID_FONTID, "Font ID Invalid in this Context");
break;
#endif
#ifdef T1ERR_INVALID_PARAMETER
case T1ERR_INVALID_PARAMETER:
- i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
+ i_push_error(T1ERR_INVALID_PARAMETER, "Invalid Argument in Function Call");
break;
#endif
#ifdef T1ERR_OP_NOT_PERMITTED
case T1ERR_OP_NOT_PERMITTED:
- i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
+ i_push_error(T1ERR_OP_NOT_PERMITTED, "Operation not Permitted");
break;
#endif
#ifdef T1ERR_ALLOC_MEM
case T1ERR_ALLOC_MEM:
- i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
+ i_push_error(T1ERR_ALLOC_MEM, "Memory Allocation Error");
break;
#endif
#ifdef T1ERR_FILE_OPEN_ERR
case T1ERR_FILE_OPEN_ERR:
- i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
+ i_push_error(T1ERR_FILE_OPEN_ERR, "Error Opening File");
break;
#endif
#ifdef T1ERR_UNSPECIFIED
case T1ERR_UNSPECIFIED:
- i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
+ i_push_error(T1ERR_UNSPECIFIED, "Unspecified T1Lib Error");
break;
#endif
#ifdef T1ERR_NO_AFM_DATA
case T1ERR_NO_AFM_DATA:
- i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
+ i_push_error(T1ERR_NO_AFM_DATA, "Missing AFM Data");
break;
#endif
#ifdef T1ERR_X11
case T1ERR_X11:
- i_push_error(T1ERR_X11, "X11");
+ i_push_error(T1ERR_X11, "X11 Interface Error");
break;
#endif
#ifdef T1ERR_COMPOSITE_CHAR
case T1ERR_COMPOSITE_CHAR:
- i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
+ i_push_error(T1ERR_COMPOSITE_CHAR, "Missing Component of Composite Character");
+ break;
+#endif
+
+#ifdef T1ERR_SCAN_ENCODING
+ case T1ERR_SCAN_ENCODING:
+ i_push_error(T1ERR_SCAN_ENCODING, "Error Scanning Encoding File");
break;
#endif
View
24 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
View
81 T1/t/t10type1.t
@@ -2,13 +2,13 @@
use strict;