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
===========
+Imager-Font-FT2 0.85
+====================
+
+ - improve thread safety
+
Imager-Font-FT2 0.85
====================
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);
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'},
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 = @_;
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;
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;
sub face_name {
my ($self) = @_;
+ $self->_valid
+ or return;
+
i_ft2_face_name($self->{id});
}
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");
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);
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]+\)$/
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__
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
+ i_ft2_start();
MANIFEST.SKIP
README
t/t10ft2.t
+t/t20thread.t
typemap
$opts{PREREQ_PM} =
{
@Imager_req,
+ 'Scalar::Util' => 1.00,
XSLoader => 0,
};
}
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;
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");
result = mymalloc(sizeof(FT2_Fonthandle));
result->face = face;
+ result->state = ft2;
result->xdpi = result->ydpi = 72;
result->encoding = encoding;
if (xdpi > 0 && ydpi > 0) {
handle->xdpi = xdpi;
handle->ydpi = ydpi;
- return 0;
+ return 1;
}
else {
i_push_error(0, "resolutions must be positive");
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);
--- /dev/null
+#!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();
use vars qw($VERSION @ISA);
BEGIN {
- $VERSION = "0.84";
+ $VERSION = "0.85";
require XSLoader;
XSLoader::load('Imager::File::GIF', $VERSION);
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
PERL_INITIALIZE_IMAGER_PERL_CALLBACKS;
+ i_init_gif();
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
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
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();
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;
}
/*
i_img *
i_readgif_single_wiol(io_glue *ig, int page) {
GifFileType *GifFile;
+ i_img *result;
i_clear_error();
if (page < 0) {
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;
}
/*
GifFileType *GifFile;
int result;
+ i_mutex_lock(mutex);
+
i_clear_error();
gif_set_version(quant, imgs, count);
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;
Arnar M. Hrafnkelsson, addi@umich.edu
+Tony Cook <tonyc@cpan.org>
+
=head1 SEE ALSO
perl(1), Imager(3)
#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);
L<Imager::Security> - brief security notes.
+=item *
+
+L<Imager::Threads> - brief information on working with threads.
+
=back
=head2 Basic Overview
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>,
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.
#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;
}
}
-
/* 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
#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
use vars qw($VERSION @ISA);
BEGIN {
- $VERSION = "0.84";
+ $VERSION = "0.85";
require XSLoader;
XSLoader::load('Imager::File::JPEG', $VERSION);
#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 */
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;
typedef wiol_source_mgr *wiol_src_ptr;
typedef wiol_destination_mgr *wiol_dest_ptr;
-
/*
* Methods for io manager objects
*
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];
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;
if (src_set)
wiol_term_source(&cinfo);
jpeg_destroy_decompress(&cinfo);
- *iptc_itext=NULL;
- *itlength=0;
if (line_buffer)
myfree(line_buffer);
if (im)
}
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);
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;
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
- *itlength=tlength;
i_tags_set(&im->tags, "i_format", "jpeg", 4);
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
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
FT2/Makefile.PL
FT2/README
FT2/t/t10ft2.t
+FT2/t/t20thread.t
FT2/typemap
gaussian.im
GIF/GIF.pm
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
MANIFEST.SKIP
map.c
maskimg.c
+mutexnull.c
+mutexpthr.c
+mutexwin.c
palimg.c
paste.im
plug.h
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
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
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,
"verbose|v" => \$VERBOSE,
"nolog" => \$NOLOG,
'coverage' => \$coverage,
- "assert|a" => \$assert);
+ "assert|a" => \$assert,
+ "tracecontext" => \$trace_context);
setenv();
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',
&& !-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
+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
======================
t/t20oo.t
T1.pm
T1.xs
+typemap
@ISA = qw(Imager::Font);
BEGIN {
- $VERSION = "1.017";
+ $VERSION = "1.018";
require XSLoader;
XSLoader::load('Imager::Font::T1', $VERSION);
*_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;
$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;
$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);
}
$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 {
sub face_name {
my ($self) = @_;
- i_t1_face_name($self->{id});
+ return $self->{t1font}->face_name();
}
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;
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
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;
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
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;
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:
#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]));
}
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:
--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 {
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
+ i_t1_start();
\ No newline at end of file
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)
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();
return(1);
}
T1_SetLogLevel(T1LOG_DEBUG);
- i_t1_set_aa(1); /* Default Antialias value */
++t1_initialized;
void
i_close_t1(void) {
+ i_mutex_lock(mutex);
T1_CloseLib();
t1_initialized = 0;
+ i_mutex_unlock(mutex);
}
=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) {
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;
}
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 );
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
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;
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));
}
im->ch_mask=ch_mask_store;
+
+ i_mutex_unlock(mutex);
+
return 1;
}
*/
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) {
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
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;
/* 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));
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;
}
*/
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;
}
c = i_utf8_advance(&text, &len);
if (c == ~0UL) {
i_push_error(0, "invalid UTF8 character");
+ i_mutex_unlock(mutex);
return 0;
}
}
++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
*/
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;
}
}
#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
#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
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");
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);
}
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";
$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
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";
"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
# 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");
}
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';
-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');
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;
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();
--- /dev/null
+Imager::Font::T1xs T_PTROBJ
+Imager-File-TIFF 0.85
+=====================
+
+ - for libtiff versions that support extended warning handlers (3.8.0
+ or later), use them to avoid some global variables.
+
Imager-File-TIFF 0.83
=====================
use vars qw($VERSION @ISA);
BEGIN {
- $VERSION = "0.83";
+ $VERSION = "0.84";
require XSLoader;
XSLoader::load('Imager::File::TIFF', $VERSION);
BOOT:
PERL_INITIALIZE_IMAGER_CALLBACKS;
+ i_tiff_init();
\ No newline at end of file
static const int text_tag_count =
sizeof(text_tag_names) / sizeof(*text_tag_names);
+#if TIFFLIB_VERSION >= 20051230
+#define USE_EXT_WARN_HANDLER
+#endif
+
+#define TIFFIO_MAGIC 0xC6A340CC
+
static void error_handler(char const *module, char const *fmt, va_list ap) {
mm_log((1, "tiff error fmt %s\n", fmt));
i_push_errorvf(0, fmt, ap);
}
+typedef struct {
+ unsigned magic;
+ io_glue *ig;
+#ifdef USE_EXT_WARN_HANDLER
+ char *warn_buffer;
+ size_t warn_size;
+#endif
+} tiffio_context_t;
+
+static void
+tiffio_context_init(tiffio_context_t *c, io_glue *ig);
+static void
+tiffio_context_final(tiffio_context_t *c);
+
#define WARN_BUFFER_LIMIT 10000
+
+#ifdef USE_EXT_WARN_HANDLER
+
+static void
+warn_handler_ex(thandle_t h, const char *module, const char *fmt, va_list ap) {
+ tiffio_context_t *c = (tiffio_context_t *)h;
+ char buf[200];
+
+ if (c->magic != TIFFIO_MAGIC)
+ return;
+
+ buf[0] = '\0';
+#ifdef IMAGER_VSNPRINTF
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+#else
+ vsprintf(buf, fmt, ap);
+#endif
+ mm_log((1, "tiff warning %s\n", buf));
+
+ if (!c->warn_buffer || strlen(c->warn_buffer)+strlen(buf)+2 > c->warn_size) {
+ size_t new_size = c->warn_size + strlen(buf) + 2;
+ char *old_buffer = c->warn_buffer;
+ if (new_size > WARN_BUFFER_LIMIT) {
+ new_size = WARN_BUFFER_LIMIT;
+ }
+ c->warn_buffer = myrealloc(c->warn_buffer, new_size);
+ if (!old_buffer) c->warn_buffer[0] = '\0';
+ c->warn_size = new_size;
+ }
+ if (strlen(c->warn_buffer)+strlen(buf)+2 <= c->warn_size) {
+ strcat(c->warn_buffer, buf);
+ strcat(c->warn_buffer, "\n");
+ }
+}
+
+#else
+
static char *warn_buffer = NULL;
static int warn_buffer_size = 0;
}
}
+#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
static
toff_t
comp_seek(thandle_t h, toff_t o, int w) {
- io_glue *ig = (io_glue*)h;
+ io_glue *ig = ((tiffio_context_t *)h)->ig;
return (toff_t) i_io_seek(ig, o, w);
}
static tsize_t
comp_read(thandle_t h, tdata_t p, tsize_t size) {
- return i_io_read((io_glue *)h, p, size);
+ return i_io_read(((tiffio_context_t *)h)->ig, p, size);
}
static tsize_t
comp_write(thandle_t h, tdata_t p, tsize_t size) {
- return i_io_write((io_glue *)h, p, size);
+ return i_io_write(((tiffio_context_t *)h)->ig, p, size);
}
static int
comp_close(thandle_t h) {
- return i_io_close((io_glue *)h);
+ return i_io_close(((tiffio_context_t *)h)->ig);
}
static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
}
i_tags_set(&im->tags, "i_format", "tiff", 4);
+#ifdef USE_EXT_WARN_HANDLER
+ {
+ tiffio_context_t *ctx = TIFFClientdata(tif);
+ if (ctx->warn_buffer && ctx->warn_buffer[0]) {
+ i_tags_set(&im->tags, "i_warning", ctx->warn_buffer, -1);
+ ctx->warn_buffer[0] = '\0';
+ }
+ }
+#else
if (warn_buffer && *warn_buffer) {
i_tags_set(&im->tags, "i_warning", warn_buffer, -1);
*warn_buffer = '\0';
}
+#endif
for (i = 0; i < compress_value_count; ++i) {
if (compress_values[i].tag == compress) {
TIFF* tif;
TIFFErrorHandler old_handler;
TIFFErrorHandler old_warn_handler;
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFErrorHandlerExt old_ext_warn_handler;
+#endif
i_img *im;
int current_page;
+ tiffio_context_t ctx;
+
+ i_mutex_lock(mutex);
i_clear_error();
old_handler = TIFFSetErrorHandler(error_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ old_warn_handler = TIFFSetWarningHandler(NULL);
+ old_ext_warn_handler = TIFFSetWarningHandlerExt(warn_handler_ex);
+#else
old_warn_handler = TIFFSetWarningHandler(warn_handler);
if (warn_buffer)
*warn_buffer = '\0';
+#endif
/* Add code to get the filename info from the iolayer */
/* Also add code to check for mmapped code */
mm_log((1, "i_readtiff_wiol(ig %p, allow_incomplete %d, page %d)\n", ig, allow_incomplete, page));
+ tiffio_context_init(&ctx, ig);
tif = TIFFClientOpen("(Iolayer)",
"rm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
i_push_error(0, "Error opening file");
TIFFSetErrorHandler(old_handler);
TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return NULL;
}
i_push_errorf(0, "could not switch to page %d", page);
TIFFSetErrorHandler(old_handler);
TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
TIFFClose(tif);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return NULL;
}
}
if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
TIFFSetErrorHandler(old_handler);
TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
TIFFClose(tif);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
+
return im;
}
TIFF* tif;
TIFFErrorHandler old_handler;
TIFFErrorHandler old_warn_handler;
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFErrorHandlerExt old_ext_warn_handler;
+#endif
i_img **results = NULL;
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
+ old_warn_handler = TIFFSetWarningHandler(NULL);
+ old_ext_warn_handler = TIFFSetWarningHandlerExt(warn_handler_ex);
+#else
old_warn_handler = TIFFSetWarningHandler(warn_handler);
if (warn_buffer)
*warn_buffer = '\0';
+#endif
+
+ tiffio_context_init(&ctx, ig);
/* Add code to get the filename info from the iolayer */
/* Also add code to check for mmapped code */
tif = TIFFClientOpen("(Iolayer)",
"rm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
i_push_error(0, "Error opening file");
TIFFSetErrorHandler(old_handler);
TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return NULL;
}
TIFFSetWarningHandler(old_warn_handler);
TIFFSetErrorHandler(old_handler);
+#ifdef USE_EXT_WARN_HANDLER
+ TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
TIFFClose(tif);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
+
return results;
}
TIFF* tif;
TIFFErrorHandler old_handler;
int i;
+ tiffio_context_t ctx;
+
+ i_mutex_lock(mutex);
old_handler = TIFFSetErrorHandler(error_handler);
mm_log((1, "i_writetiff_multi_wiol(ig %p, imgs %p, count %d)\n",
ig, imgs, count));
- /* FIXME: Enable the mmap interface */
+ tiffio_context_init(&ctx, ig);
tif = TIFFClientOpen("No name",
"wm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n"));
i_push_error(0, "Could not create TIFF object");
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
if (!i_writetiff_low(tif, imgs[i])) {
TIFFClose(tif);
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
i_push_error(0, "Cannot write TIFF directory");
TIFFClose(tif);
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
}
TIFFSetErrorHandler(old_handler);
(void) TIFFClose(tif);
+ tiffio_context_final(&ctx);
+
+ i_mutex_unlock(mutex);
if (i_io_close(ig))
return 0;
TIFF* tif;
int i;
TIFFErrorHandler old_handler;
+ tiffio_context_t ctx;
+
+ i_mutex_lock(mutex);
old_handler = TIFFSetErrorHandler(error_handler);
mm_log((1, "i_writetiff_multi_wiol(ig %p, imgs %p, count %d)\n",
ig, imgs, count));
- /* FIXME: Enable the mmap interface */
+ tiffio_context_init(&ctx, ig);
tif = TIFFClientOpen("No name",
"wm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
i_push_error(0, "Could not create TIFF object");
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
TIFFClose(tif);
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
i_push_error(0, "Cannot write TIFF directory");
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;
i_writetiff_wiol(i_img *img, io_glue *ig) {
TIFF* tif;
TIFFErrorHandler old_handler;
+ tiffio_context_t ctx;
+
+ i_mutex_lock(mutex);
old_handler = TIFFSetErrorHandler(error_handler);
i_clear_error();
mm_log((1, "i_writetiff_wiol(img %p, ig %p)\n", img, ig));
- /* FIXME: Enable the mmap interface */
+ tiffio_context_init(&ctx, ig);
tif = TIFFClientOpen("No name",
"wm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
if (!tif) {
mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
i_push_error(0, "Could not create TIFF object");
+ tiffio_context_final(&ctx);
TIFFSetErrorHandler(old_handler);
+ i_mutex_unlock(mutex);
return 0;
}
if (!i_writetiff_low(tif, img)) {
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);
+ i_mutex_unlock(mutex);
if (i_io_close(ig))
return 0;
i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
TIFF* tif;
TIFFErrorHandler old_handler;
+ tiffio_context_t ctx;
+
+ i_mutex_lock(mutex);
old_handler = TIFFSetErrorHandler(error_handler);
i_clear_error();
mm_log((1, "i_writetiff_wiol(img %p, ig %p)\n", im, ig));
- /* FIXME: Enable the mmap interface */
+ tiffio_context_init(&ctx, ig);
tif = TIFFClientOpen("No name",
"wm",
- (thandle_t) ig,
+ (thandle_t) &ctx,
comp_read,
comp_write,
comp_seek,
mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
i_push_error(0, "Could not create TIFF object");
TIFFSetErrorHandler(old_handler);
+ tiffio_context_final(&ctx);
+ i_mutex_unlock(mutex);
return 0;
}
if (!i_writetiff_low_faxable(tif, im, 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;
}
}
}
-
+
return 1;
}
return TIFFIsCODECConfigured(scheme);
}
+static void
+tiffio_context_init(tiffio_context_t *c, io_glue *ig) {
+ c->magic = TIFFIO_MAGIC;
+ c->ig = ig;
+#ifdef USE_EXT_WARN_HANDLER
+ c->warn_buffer = NULL;
+ c->warn_size = 0;
+#endif
+}
+
+static void
+tiffio_context_final(tiffio_context_t *c) {
+ c->magic = TIFFIO_MAGIC;
+#ifdef USE_EXT_WARN_HANDLER
+ if (c->warn_buffer)
+ myfree(c->warn_buffer);
+#endif
+}
+
/*
=back
#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);
=head1 SEE ALSO
-Imager, Imager::ExtUtils, Imager::Inline
+Imager, Imager::API, Imager::ExtUtils, Imager::Inline
=cut
EOS
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 && /\(\*f_(i[om]?_\w+)/) {
my $name = $1;
$name =~ s/_imp$//;
push @funcs, $name;
--- /dev/null
+#!perl -w
+use strict;
+use threads;
+use Imager;
+
+++$|;
+Imager->preload;
+
+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();
+}
+#define IMAGER_NO_CONTEXT
#include <stdarg.h>
#include "imageri.h"
*/
int
i_writebmp_wiol(i_img *im, io_glue *ig) {
+ dIMCTXim(im);
i_clear_error();
/* pick a format */
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();
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,
(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;
}
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;
}
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;
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;
}
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");
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;
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;
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;
int y;
int line_size = 3 * im->xsize;
i_color bg;
+ dIMCTXim(im);
i_get_file_background(im, &bg);
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)) {
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;
}
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;
}
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 */
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;
}
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;
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;
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;
}
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) */
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;
}
}
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;
}
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';
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;
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;
}
--- /dev/null
+#include "imageri.h"
+#include <stdio.h>
+
+static im_slot_t slot_count;
+static im_slot_destroy_t *slot_destructors;
+static 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->slots = calloc(sizeof(void *), slot_count);
+ 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;
+
+ for (slot = 0; slot < ctx->slot_alloc; ++slot) {
+ if (ctx->slots[slot] && slot_destructors[slot])
+ slot_destructors[slot](ctx->slots[slot]);
+ }
+
+ 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)
+ fclose(ctx->lg_file);
+#endif
+
+ free(ctx);
+}
+
+/*
+=item im_context_clone(ctx)
+
+Clone an Imager context object, returning the result.
+
+=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->slots = calloc(sizeof(void *), slot_count);
+ if (!nctx->slots) {
+ free(nctx);
+ return NULL;
+ }
+ nctx->slot_alloc = slot_count;
+
+ nctx->error_sp = ctx->error_sp;
+ for (i = 0; i < IM_ERROR_COUNT; ++i) {
+ if (ctx->error_stack[i].msg) {
+ size_t sz = ctx->error_alloc[i];
+ nctx->error_alloc[i] = sz;
+ nctx->error_stack[i].msg = mymalloc(sz);
+ memcpy(nctx->error_stack[i].msg, ctx->error_stack[i].msg, sz);
+ }
+ else {
+ nctx->error_alloc[i] = 0;
+ nctx->error_stack[i].msg = NULL;
+ }
+ nctx->error_stack[i].code = ctx->error_stack[i].code;
+ }
+#ifdef IMAGER_LOG
+ nctx->log_level = ctx->log_level;
+ if (ctx->lg_file) {
+ /* disable buffering, this isn't perfect */
+ setvbuf(ctx->lg_file, NULL, _IONBF, 0);
+
+ /* clone that and disable buffering some more */
+ nctx->lg_file = fdopen(fileno(ctx->lg_file), "a");
+ if (nctx->lg_file)
+ setvbuf(nctx->lg_file, NULL, _IONBF, 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.
+
+=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];
+}
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "imageri.h"
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;
}
=cut
*/
+#define IMAGER_NO_CONTEXT
#include "imager.h"
struct chan_copy {
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;
}
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);
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#define IMAGER_NO_CONTEXT
#include "imager.h"
/*
}
}
/* 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");
}
}
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "draw.h"
#include "log.h"
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);
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);
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);
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);
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);
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;
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;
}
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)
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;
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)
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);
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
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
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;
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;
}
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;
}
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;
}
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;
}
typedef void *minthandle_t;
#endif
-#include "plug.h"
+#include "ext.h"
struct DSO_handle_tag {
minthandle_t handle;
/* #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,
=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;
}
/*
=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
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.
=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
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);
*/
vsprintf(buf, fmt, ap);
#endif
- i_push_error(code, buf);
+ im_push_error(ctx, code, buf);
}
/*
=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"
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();
}
/*
-#include "imager.h"
+#include "imdatatypes.h"
#ifndef IMAGER_EXT_H
#define IMAGER_EXT_H
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
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "imageri.h"
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "imageri.h"
#include <stdlib.h>
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;
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;
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;
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;
}
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;
}
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;
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]);
}
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];
}
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]);
}
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++) {
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;
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();
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++) {
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;
i_img *out;
int outchans, diffchans;
i_img_dim xsize, ysize;
+ dIMCTXim(im1);
i_clear_error();
if (im1->channels != im2->channels) {
i_fountain_seg *my_segs;
i_fill_combine_f combine_func = NULL;
i_fill_combinef_f combinef_func = NULL;
+ dIMCTXim(im);
i_clear_error();
+#define IMAGER_NO_CONTEXT
#include "imager.h"
static void flip_h(i_img *im);
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;
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;
}
+++ /dev/null
-#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
-*/
--- /dev/null
+#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
+*/
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include <math.h>
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) {
+#define IMAGER_NO_CONTEXT
#include "imageri.h"
#include <stdlib.h>
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;
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 */
+#define IMAGER_NO_CONTEXT
+
#include "imager.h"
#include "imageri.h"
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
=cut
*/
+im_context_t (*im_get_context)(void) = NULL;
+
#define XAXIS 0
#define YAXIS 1
#define XYAXIS 2
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.
*/
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");
}
/*
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;
}
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;
}
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));
}
/*
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);
}
*/
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;
}
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);
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");
}
/*
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;
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));
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;
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);
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;
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;
}
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) {
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;
}
=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);
=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);
}
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 ;
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;
}
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;
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;
}
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;
tdiff += sdiff * sdiff;
}
}
- mm_log((1,"i_img_diffd <- (%.2f)\n",tdiff));
+ im_log((aIMCTX, 1,"i_img_diffd <- (%.2f)\n",tdiff));
return tdiff;
}
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++) {
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;
}
int ch,c;
i_img *new_img,*new_img2;
i_color val1,val2,dval1,dval2;
+ dIMCTXim(im);
mx=im->xsize;
my=im->ysize;
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;
/* 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;
}
}
#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
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);
/* 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);
/* 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);
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);
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 */
/* 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);
#endif /* IMAGER_MALLOC_DEBUG */
#include "imrender.h"
-#include "immacros.h"
extern void
i_adapt_colors(int dest_channels, int src_channels, i_color *colors,
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
#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
#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
+ int log_level;
+ FILE *lg_file;
+ 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
#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
*/
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
i_f_psampf_t i_f_psampf;
void *im_data;
+
+ /* 0.91 */
+ im_context_t context;
};
/* ext_data for paletted images
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.
#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
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
#include "imager.h"
#include "imio.h"
+static im_context_t get_context(void);
+
/*
DON'T ADD CASTS TO THESE
*/
myfree_file_line,
myrealloc_file_line,
- i_img_8_new,
- i_img_16_new,
- i_img_double_new,
- i_img_pal_new,
+ im_img_8_new,
+ im_img_16_new,
+ im_img_double_new,
+ im_img_pal_new,
i_img_destroy,
i_sametype,
i_sametype_chans,
i_quant_translate,
i_quant_transparent,
- i_clear_error,
- i_push_error,
+ im_clear_error,
+ im_push_error,
i_push_errorf,
- i_push_errorvf,
+ im_push_errorvf,
i_tags_new,
i_tags_set,
i_rubthru,
/* IMAGER_API_LEVEL 2 functions */
- i_set_image_file_limits,
- i_get_image_file_limits,
- i_int_check_image_file_limits,
+ im_set_image_file_limits,
+ im_get_image_file_limits,
+ im_int_check_image_file_limits,
i_flood_fill_border,
i_flood_cfill_border,
i_loog,
/* IMAGER_API_LEVEL 4 functions */
- i_img_alloc,
- i_img_init,
+ im_img_alloc,
+ im_img_init,
/* IMAGER_API_LEVEL 5 functions */
i_img_is_monochrome,
i_io_close,
i_io_set_buffered,
i_io_gets,
- io_new_fd,
- io_new_bufchain,
- io_new_buffer,
- io_new_cb,
+ im_io_new_fd,
+ im_io_new_bufchain,
+ im_io_new_buffer,
+ im_io_new_cb,
io_slurp,
- io_glue_destroy
+ io_glue_destroy,
+
+ /* level 8 */
+ get_context,
+ im_push_errorf,
+ 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
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();
+}
/* 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__))
#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)))
#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)))
#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) \
#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))
#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)))
#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
Version 5 changed the return types of i_get_file_background() and
i_get_file_backgroundf() from void to int.
+ Version 6 added
+
*/
-#define IMAGER_API_VERSION 5
+#define IMAGER_API_VERSION 6
/*
IMAGER_API_LEVEL is the level of the structure. New function pointers
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_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_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);
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_im_clear_error)(im_context_t ctx);
+ void (*f_im_push_error)(im_context_t ctx, int code, char const *msg);
void (*f_i_push_errorf)(int code, char const *fmt, ...);
- void (*f_i_push_errorvf)(int code, char const *fmt, va_list);
+ void (*f_im_push_errorvf)(im_context_t ctx, int code, char const *fmt, va_list);
void (*f_i_tags_new)(i_img_tags *tags);
int (*f_i_tags_set)(i_img_tags *tags, char const *name, char const *data,
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_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);
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);
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_im_img_alloc)(im_context_t ctx);
+ void (*f_im_img_init)(im_context_t ctx, i_img *);
/* IMAGER_API_LEVEL 5 functions will be added here */
/* added i_psampf?_bits macros */
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_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);
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 */
-
+ im_context_t (*f_im_get_context)(void);
+
+ void (*f_im_push_errorf)(im_context_t , int code, char const *fmt, ...);
+ 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"
=cut
*/
+#define IMAGER_NO_CONTEXT
+
#include "imager.h"
#include "imageri.h"
((((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;
}
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;
im->idata = mymalloc(im->bytes);
memset(im->idata, 0, im->bytes);
- i_img_init(im);
+ im_img_init(aIMCTX, im);
return 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);
/* 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;
}
}
}
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;
}
/* 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;
}
}
}
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;
}
/* 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;
}
}
}
else {
if (chan_count <= 0 || chan_count > im->channels) {
+ dIMCTXim(im);
i_push_error(0, "Invalid channel count");
return -1;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
i_img_dim off;
if (bits != 16) {
+ dIMCTXim(im);
i_push_error(0, "Invalid bits for 16-bit image");
return -1;
}
/* 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;
}
}
}
else {
if (chan_count <= 0 || chan_count > im->channels) {
+ dIMCTXim(im);
i_push_error(0, "Invalid channel count");
return -1;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
+#define IMAGER_NO_CONTEXT
+
#include "imager.h"
#include "imageri.h"
}*/
/*
-=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
=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;
}
*/
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);
}
/*
*/
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);
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;
}
/* 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;
}
}
}
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;
}
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) {
/* 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;
}
}
}
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;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
=cut
*/
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "imageri.h"
};
/*
-=item i_img_double_new(i_img_dim x, i_img_dim y, int ch)
+=item im_img_double_new(ctx, x, y, ch)
+X<im_img_double_new API>X<i_img_double_new API>
=category Image creation/destruction
+=synopsis i_img *img = im_img_double_new(aIMCTX, width, height, channels);
=synopsis i_img *img = i_img_double_new(width, height, channels);
Creates a new double per sample image.
+Also callable as C<i_img_double_new(width, height, channels)>.
+
=cut
*/
-i_img *i_img_double_new(i_img_dim x, i_img_dim y, int ch) {
+i_img *
+im_img_double_new(pIMCTX, i_img_dim x, i_img_dim y, int ch) {
size_t bytes;
i_img *im;
- mm_log((1,"i_img_double_new(x %" i_DF ", y %" i_DF ", ch %d)\n",
+ im_log((aIMCTX, 1,"i_img_double_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 * sizeof(double);
if (bytes / y / ch / sizeof(double) != x) {
- i_push_errorf(0, "integer overflow calculating image allocation");
+ im_push_errorf(aIMCTX, 0, "integer overflow calculating image allocation");
return NULL;
}
- im = i_img_alloc();
+ im = im_img_alloc(aIMCTX);
*im = IIM_base_double_direct;
i_tags_new(&im->tags);
im->xsize = x;
im->ext_data = NULL;
im->idata = mymalloc(im->bytes);
memset(im->idata, 0, im->bytes);
- i_img_init(im);
+ im_img_init(aIMCTX, im);
return im;
}
/* 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;
}
}
}
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;
}
/* 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;
}
}
}
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;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
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))
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
i_img *targ;
i_fcolor *line;
i_img_dim y;
+ dIMCTXim(im);
- targ = i_img_double_new(im->xsize, im->ysize, im->channels);
+ targ = im_img_double_new(aIMCTX, im->xsize, im->ysize, im->channels);
if (!targ)
return NULL;
line = mymalloc(sizeof(i_fcolor) * im->xsize);
#define i_psampf(im, l, r, y, samps, chans, count) \
(((im)->i_f_psampf)((im), (l), (r), (y), (samps), (chans), (count)))
+#ifndef IMAGER_DIRECT_IMAGE_CALLS
+#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))
+
+#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)))
+
+#endif
+
+#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)
+
+#define pIMCTX im_context_t my_im_ctx
+
+#ifdef IMAGER_NO_CONTEXT
+#define dIMCTXctx(ctx) pIMCTX = (ctx)
+#define dIMCTX dIMCTXctx(im_get_context())
+#define dIMCTXim(im) dIMCTXctx((im)->context)
+#define dIMCTXio(io) dIMCTXctx((io)->context)
+#define aIMCTX my_im_ctx
+#else
+#define aIMCTX im_get_context()
+#endif
+
+#define i_img_8_new(xsize, ysize, channels) im_img_8_new(aIMCTX, (xsize), (ysize), (channels))
+#define i_img_16_new(xsize, ysize, channels) im_img_16_new(aIMCTX, (xsize), (ysize), (channels))
+#define i_img_double_new(xsize, ysize, channels) im_img_double_new(aIMCTX, (xsize), (ysize), (channels))
+#define i_img_pal_new(xsize, ysize, channels, maxpal) im_img_pal_new(aIMCTX, (xsize), (ysize), (channels), (maxpal))
+
+#define i_img_alloc() im_img_alloc(aIMCTX)
+#define i_img_init(im) im_img_init(aIMCTX, im)
+
+#define i_set_image_file_limits(width, height, bytes) im_set_image_file_limits(aIMCTX, width, height, bytes)
+#define i_get_image_file_limits(width, height, bytes) im_get_image_file_limits(aIMCTX, width, height, bytes)
+#define i_int_check_image_file_limits(width, height, channels, sample_size) im_int_check_image_file_limits(aIMCTX, width, height, channels, sample_size)
+
+#define i_clear_error() im_clear_error(aIMCTX)
+#define i_push_errorvf(code, fmt, args) im_push_errorvf(aIMCTX, code, fmt, args)
+#define i_push_error(code, msg) im_push_error(aIMCTX, code, msg)
+#define i_errors() im_errors(aIMCTX)
+
+#define io_new_fd(fd) im_io_new_fd(aIMCTX, (fd))
+#define io_new_bufchain() im_io_new_bufchain(aIMCTX)
+#define io_new_buffer(data, len, closecb, closectx) im_io_new_buffer(aIMCTX, (data), (len), (closecb), (closectx))
+#define io_new_cb(p, readcb, writecb, seekcb, closecb, destroycb) \
+ im_io_new_cb(aIMCTX, (p), (readcb), (writecb), (seekcb), (closecb), (destroycb))
+
#endif
+#define IMAGER_NO_CONTEXT
#include "imager.h"
#include "iolayer.h"
#include "imerror.h"
*/
static void
-i_io_init(io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb,
- i_io_seekp_t seekcb);
+i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb,
+ i_io_writep_t writecb, i_io_seekp_t seekcb);
static ssize_t fd_read(io_glue *ig, void *buf, size_t count);
static ssize_t fd_write(io_glue *ig, const void *buf, size_t count);
*/
/*
-=item io_new_bufchain()
+=item im_io_new_bufchain(ctx)
+X<im_io_new_bufchain API>X<i_io_new_bufchain API>
=order 10
=category I/O Layers
-returns a new io_glue object that has the 'empty' source and but can
+Returns a new io_glue object that has the 'empty' source and but can
be written to and read from later (like a pseudo file).
+Also callable as C<io_new_bufchain()>.
+
=cut
*/
io_glue *
-io_new_bufchain() {
+im_io_new_bufchain(pIMCTX) {
io_glue *ig;
io_ex_bchain *ieb = mymalloc(sizeof(io_ex_bchain));
- mm_log((1, "io_new_bufchain()\n"));
+ im_log((aIMCTX, 1, "io_new_bufchain()\n"));
ig = mymalloc(sizeof(io_glue));
memset(ig, 0, sizeof(*ig));
- i_io_init(ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek);
+ i_io_init(aIMCTX, ig, BUFCHAIN, bufchain_read, bufchain_write, bufchain_seek);
ieb->offset = 0;
ieb->length = 0;
ig->closecb = bufchain_close;
ig->destroycb = bufchain_destroy;
+ im_context_refinc(aIMCTX, "im_io_new_bufchain");
+
return ig;
}
/*
-=item io_new_buffer(data, length)
+=item im_io_new_buffer(ctx, data, length)
+X<im_io_new_buffer API>X<io_new_buffer API>
=order 10
=category I/O Layers
Returns a new io_glue object that has the source defined as reading
from specified buffer. Note that the buffer is not copied.
+ ctx - an Imager context object
data - buffer to read from
length - length of buffer
+Also callable as C<io_new_buffer(data, length>.
+
=cut
*/
io_glue *
-io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) {
+im_io_new_buffer(pIMCTX, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata) {
io_buffer *ig;
- mm_log((1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata));
+ im_log((aIMCTX, 1, "io_new_buffer(data %p, len %ld, closecb %p, closedata %p)\n", data, (long)len, closecb, closedata));
ig = mymalloc(sizeof(io_buffer));
memset(ig, 0, sizeof(*ig));
- i_io_init(&ig->base, BUFFER, buffer_read, buffer_write, buffer_seek);
+ i_io_init(aIMCTX, &ig->base, BUFFER, buffer_read, buffer_write, buffer_seek);
ig->data = data;
ig->len = len;
ig->closecb = closecb;
ig->base.closecb = buffer_close;
ig->base.destroycb = buffer_destroy;
+ im_context_refinc(aIMCTX, "im_io_new_bufchain");
+
return (io_glue *)ig;
}
/*
-=item io_new_fd(fd)
+=item im_io_new_fd(ctx, file)
+X<io_new_fd API>X<im_io_new_fd API>
=order 10
=category I/O Layers
-returns a new io_glue object that has the source defined as reading
+Returns a new io_glue object that has the source defined as reading
from specified file descriptor. Note that the the interface to receiving
data from the io_glue callbacks hasn't been done yet.
- fd - file descriptor to read/write from
+ ctx - and Imager context object
+ file - file descriptor to read/write from
+
+Also callable as C<io_new_fd(file)>.
=cut
*/
io_glue *
-io_new_fd(int fd) {
+im_io_new_fd(pIMCTX, int fd) {
io_fdseek *ig;
- mm_log((1, "io_new_fd(fd %d)\n", fd));
+ im_log((aIMCTX, 1, "io_new_fd(fd %d)\n", fd));
ig = mymalloc(sizeof(io_fdseek));
memset(ig, 0, sizeof(*ig));
- i_io_init(&ig->base, FDSEEK, fd_read, fd_write, fd_seek);
+ i_io_init(aIMCTX, &ig->base, FDSEEK, fd_read, fd_write, fd_seek);
ig->fd = fd;
ig->base.closecb = fd_close;
ig->base.sizecb = fd_size;
ig->base.destroycb = NULL;
+ im_context_refinc(aIMCTX, "im_io_new_bufchain");
- mm_log((1, "(%p) <- io_new_fd\n", ig));
+ im_log((aIMCTX, 1, "(%p) <- io_new_fd\n", ig));
return (io_glue *)ig;
}
/*
-=item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb)
+=item im_io_new_cb(ctx, p, read_cb, write_cb, seek_cb, close_cb, destroy_cb)
+X<im_io_new_cb API>X<io_new_cb API>
=category I/O Layers
=order 10
=back
+Also callable as C<io_new_cb(p, readcb, writecb, seekcb, closecb,
+destroycb)>.
+
=cut
*/
io_glue *
-io_new_cb(void *p, i_io_readl_t readcb, i_io_writel_t writecb,
+im_io_new_cb(pIMCTX, 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) {
io_cb *ig;
- mm_log((1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, "
+ im_log((aIMCTX, 1, "io_new_cb(p %p, readcb %p, writecb %p, seekcb %p, closecb %p, "
"destroycb %p)\n", p, readcb, writecb, seekcb, closecb, destroycb));
ig = mymalloc(sizeof(io_cb));
memset(ig, 0, sizeof(*ig));
- i_io_init(&ig->base, CBSEEK, realseek_read, realseek_write, realseek_seek);
- mm_log((1, "(%p) <- io_new_cb\n", ig));
+ i_io_init(aIMCTX, &ig->base, CBSEEK, realseek_read, realseek_write, realseek_seek);
+ im_log((aIMCTX, 1, "(%p) <- io_new_cb\n", ig));
ig->base.closecb = realseek_close;
ig->base.destroycb = realseek_destroy;
ig->seekcb = seekcb;
ig->closecb = closecb;
ig->destroycb = destroycb;
+
+ im_context_refinc(aIMCTX, "im_io_new_bufchain");
return (io_glue *)ig;
}
/*
=item io_slurp(ig, c)
+X<io_slurp API>
=category I/O Layers
Takes the source that the io_glue is bound to and allocates space for
io_type inn = ig->type;
if ( inn != BUFCHAIN ) {
- i_fatal(0, "io_slurp: called on a source that is not from a bufchain\n");
+ dIMCTXio(ig);
+ im_fatal(aIMCTX, 0, "io_slurp: called on a source that is not from a bufchain\n");
}
ieb = ig->exdata;
rc = bufchain_read(ig, cc, ieb->length);
- if (rc != ieb->length)
- i_fatal(1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
+ if (rc != ieb->length) {
+ dIMCTXio(ig);
+ im_fatal(aIMCTX,1, "io_slurp: bufchain_read returned an incomplete read: rc = %d, request was %d\n", rc, ieb->length);
+ }
return rc;
}
/*
=item io_glue_destroy(ig)
+X<io_glue_destroy API>
=category I/O Layers
=order 90
=synopsis io_glue_destroy(ig);
void
io_glue_destroy(io_glue *ig) {
- mm_log((1, "io_glue_DESTROY(ig %p)\n", ig));
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "io_glue_DESTROY(ig %p)\n", ig));
if (ig->destroycb)
ig->destroycb(ig);
myfree(ig->buffer);
myfree(ig);
+
+ im_context_refdec(aIMCTX, "io_glue_destroy");
}
/*
IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn(%p, %p, %d)\n", ig, buf, (int)size));
if (size == 0) {
+ dIMCTXio(ig);
i_push_error(0, "peekn size must be positive");
IOL_DEB(fprintf(IOL_DEBs, "i_io_peekn() => -1 (zero size)\n"));
return -1;
*/
static void
-i_io_init(io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb,
+i_io_init(pIMCTX, io_glue *ig, int type, i_io_readp_t readcb, i_io_writep_t writecb,
i_io_seekp_t seekcb) {
ig->type = type;
ig->exdata = NULL;
ig->closecb = NULL;
ig->sizecb = NULL;
ig->destroycb = NULL;
+ ig->context = aIMCTX;
ig->buffer = NULL;
ig->read_ptr = NULL;
int
realseek_close(io_glue *igo) {
io_cb *ig = (io_cb *)igo;
+ dIMCTXio(igo);
IOL_DEB(fprintf(IOL_DEBs, "realseek_close(%p)\n", ig));
- mm_log((1, "realseek_close(ig %p)\n", ig));
+ im_log((aIMCTX,1, "realseek_close(ig %p)\n", ig));
if (ig->closecb)
return ig->closecb(ig->p);
else
IOL_DEB( fprintf(IOL_DEBs, "buffer_read: ig->cpos = %ld, buf = %p, count = %u\n", (long) ig->cpos, buf, (unsigned)count) );
if ( ig->cpos+count > ig->len ) {
- mm_log((1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count));
+ dIMCTXio(igo);
+ im_log((aIMCTX, 1,"buffer_read: short read: cpos=%ld, len=%ld, count=%ld\n", (long)ig->cpos, (long)ig->len, (long)count));
count = ig->len - ig->cpos;
}
static
ssize_t
buffer_write(io_glue *ig, const void *buf, size_t count) {
- mm_log((1, "buffer_write called, this method should never be called.\n"));
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "buffer_write called, this method should never be called.\n"));
return -1;
}
static
int
buffer_close(io_glue *ig) {
- mm_log((1, "buffer_close(ig %p)\n", ig));
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "buffer_close(ig %p)\n", ig));
return 0;
}
calc_seek_offset(ig->cpos, ig->len, offset, whence);
if (reqpos > ig->len) {
- mm_log((1, "seeking out of readable range\n"));
+ dIMCTXio(igo);
+ im_log((aIMCTX, 1, "seeking out of readable range\n"));
return (off_t)-1;
}
if (reqpos < 0) {
+ dIMCTXio(igo);
i_push_error(0, "seek before beginning of file");
return (off_t)-1;
}
io_buffer *ig = (io_buffer *)igo;
if (ig->closecb) {
- mm_log((1,"calling close callback %p for io_buffer\n",
+ dIMCTXio(igo);
+ im_log((aIMCTX, 1,"calling close callback %p for io_buffer\n",
ig->closecb));
ig->closecb(ig->closedata);
}
io_blink_new(void) {
io_blink *ib;
- mm_log((1, "io_blink_new()\n"));
+#if 0
+ im_log((aIMCTX, 1, "io_blink_new()\n"));
+#endif
ib = mymalloc(sizeof(io_blink));
static void
io_destroy_bufchain(io_ex_bchain *ieb) {
io_blink *cp;
+#if 0
mm_log((1, "io_destroy_bufchain(ieb %p)\n", ieb));
+#endif
cp = ieb->head;
while(cp) {
size_t scount = count;
char *cbuf = buf;
size_t sk;
+ dIMCTXio(ig);
- mm_log((1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count));
+ im_log((aIMCTX, 1, "bufchain_read(ig %p, buf %p, count %ld)\n", ig, buf, (long)count));
while( scount ) {
int clen = (ieb->cp == ieb->tail) ? ieb->tfill : ieb->cp->len;
ieb->gpos += sk;
}
- mm_log((1, "bufchain_read: returning %ld\n", (long)(count-scount)));
+ im_log((aIMCTX, 1, "bufchain_read: returning %ld\n", (long)(count-scount)));
return count-scount;
}
io_ex_bchain *ieb = ig->exdata;
size_t ocount = count;
size_t sk;
+ dIMCTXio(ig);
- mm_log((1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count));
+ im_log((aIMCTX, 1, "bufchain_write: ig = %p, buf = %p, count = %ld\n", ig, buf, (long)count));
IOL_DEB( fprintf(IOL_DEBs, "bufchain_write: ig = %p, ieb->cpos = %ld, buf = %p, count = %ld\n", ig, (long) ieb->cpos, buf, (long)count) );
while(count) {
- mm_log((2, "bufchain_write: - looping - count = %ld\n", (long)count));
+ im_log((aIMCTX, 2, "bufchain_write: - looping - count = %ld\n", (long)count));
if (ieb->cp->len == ieb->cpos) {
- mm_log((1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos));
+ im_log((aIMCTX, 1, "bufchain_write: cp->len == ieb->cpos = %ld - advancing chain\n", (long) ieb->cpos));
io_bchain_advance(ieb);
}
if (ieb->cp == ieb->tail) {
int extend = ieb->cpos + sk - ieb->tfill;
- mm_log((2, "bufchain_write: extending tail by %d\n", extend));
+ im_log((aIMCTX, 2, "bufchain_write: extending tail by %d\n", extend));
if (extend > 0) {
ieb->length += extend;
ieb->tfill += extend;
static
int
bufchain_close(io_glue *ig) {
- mm_log((1, "bufchain_close(ig %p)\n",ig));
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "bufchain_close(ig %p)\n",ig));
IOL_DEB( fprintf(IOL_DEBs, "bufchain_close(ig %p)\n", ig) );
return 0;
off_t scount = calc_seek_offset(ieb->gpos, ieb->length, offset, whence);
off_t sk;
+ dIMCTXio(ig);
- mm_log((1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence));
+ im_log((aIMCTX, 1, "bufchain_seek(ig %p, offset %ld, whence %d)\n", ig, (long)offset, whence));
if (scount < 0) {
i_push_error(0, "invalid whence supplied or seek before start of file");
while(wrlen > 0) {
ssize_t rc, wl = i_min(wrlen, BBSIZ);
- mm_log((1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl));
+ im_log((aIMCTX, 1, "bufchain_seek: wrlen = %d, wl = %ld\n", wrlen, (long)wl));
rc = bufchain_write( ig, TB, wl );
- if (rc != wl) i_fatal(0, "bufchain_seek: Unable to extend file\n");
+ if (rc != wl) im_fatal(aIMCTX, 0, "bufchain_seek: Unable to extend file\n");
wrlen -= rc;
}
}
- mm_log((2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos));
+ im_log((aIMCTX, 2, "bufchain_seek: returning ieb->gpos = %ld\n", (long)ieb->gpos));
return ieb->gpos;
}
/* 0 is valid - means EOF */
if (result < 0) {
- i_push_errorf(0, "read() failure: %s (%d)", my_strerror(errno), errno);
+ dIMCTXio(igo);
+ im_push_errorf(aIMCTX, 0, "read() failure: %s (%d)", my_strerror(errno), errno);
}
return result;
(unsigned)count, (int)result));
if (result <= 0) {
- i_push_errorf(errno, "write() failure: %s (%d)", my_strerror(errno), errno);
+ dIMCTXio(igo);
+ im_push_errorf(aIMCTX, errno, "write() failure: %s (%d)", my_strerror(errno), errno);
}
return result;
#endif
if (result == (off_t)-1) {
- i_push_errorf(errno, "lseek() failure: %s (%d)", my_strerror(errno), errno);
+ dIMCTXio(igo);
+ im_push_errorf(aIMCTX, errno, "lseek() failure: %s (%d)", my_strerror(errno), errno);
}
return result;
}
static ssize_t fd_size(io_glue *ig) {
- mm_log((1, "fd_size(ig %p) unimplemented\n", ig));
+ dIMCTXio(ig);
+ im_log((aIMCTX, 1, "fd_size(ig %p) unimplemented\n", ig));
return -1;
}
void io_glue_gettypes (io_glue *ig, int reqmeth);
/* XS functions */
-io_glue *io_new_fd(int fd);
-io_glue *io_new_bufchain(void);
-io_glue *io_new_buffer(const char *data, size_t len, i_io_closebufp_t closecb, void *closedata);
-io_glue *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);
+io_glue *im_io_new_fd(pIMCTX, int fd);
+io_glue *im_io_new_bufchain(pIMCTX);
+io_glue *im_io_new_buffer(pIMCTX, const char *data, size_t len, i_io_closebufp_t closecb, void *closedata);
+io_glue *im_io_new_cb(pIMCTX, 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);
size_t io_slurp(io_glue *ig, unsigned char **c);
void io_glue_destroy(io_glue *ig);
/* if non-zero we do write buffering (enabled by default) */
int buffered;
+
+ im_context_t context;
};
#define I_IO_DUMP_CALLBACKS 1
=for stopwords XS
The API allows you to access Imager functions at the C level from XS
-and from Inline::C.
+and from C<Inline::C>.
The intent is to allow users to:
=item *
write C code that does Imager operations the user might do from Perl,
-but faster, for example, the Imager::CountColor example.
+but faster, for example, the L<Imager::CountColor> example.
=item *
=item *
-i_img - used to represent an image
+L</i_img> - used to represent an image
=item *
-i_color - used to represent a color with up to 8 bits per sample.
+L</i_color> - used to represent a color with up
+to 8 bits per sample.
=item *
-i_fcolor - used to represent a color with a double per sample.
+L</i_fcolor> - used to represent
+a color with a double per sample.
=item *
-i_fill_t - an abstract fill
+L</i_fill_t> - fill objects>> - an abstract fill
+
+=item *
+
+L</im_context_t> - Imager's per-thread state.
=back
At this point there is no consolidated font object type, and hence the
font functions are not visible through Imager's API.
-=head2 i_img - images
+=head2 i_img
This contains the dimensions of the image (C<xsize>, C<ysize>,
C<channels>), image metadata (C<ch_mask>, C<bits>, C<type>,
-C<virtual>), potentially image data (C<idata>) and the a function
-table, with pointers to functions to perform various low level image
+C<virtual>), potentially image data (C<idata>) and a function table,
+with pointers to functions to perform various low level image
operations.
The only time you should directly write to any value in this type is
Imager return type and a raw image object for an Imager::ImgRaw return
type.
-=head2 C<i_color> - 8-bit color
+=head2 i_color
Represents an 8-bit per sample color. This is a union containing
several different structs for access to components of a color:
=back
-Use Imager::Color for parameter and return value types.
+Use C<Imager::Color> for parameter and return value types.
-=head2 C<i_fcolor> - floating point color
+=head2 i_fcolor
Similar to C<i_color> except that each component is a double instead of
an unsigned char.
Use Imager::Color::Float for parameter and return value types.
-=head2 C<i_fill_t> - fill objects
+=head2 i_fill_t
Abstract type containing pointers called to perform low level fill
operations.
Perl level this is stored in the C<fill> member of the Perl level
Imager::Fill object.
+=head2 i_io_glue_t
+
+C<i_io_glue_t> is Imager's I/O abstraction.
+
+Historically named C<io_glue>, and this name is available for backward
+compatibility.
+
+=head2 im_context_t
+
+This new type is an opaque type that stores Imager's per-thread state,
+including the error message stack, the current log file state and
+image size file limits.
+
+While Imager's internal typemap provides a C<T_PTROBJ> mapping and a
+DESTROY method for this type you B<must> never return objects of this
+type back to perl.
+
+See L</Context objects> for more information.
+
=head1 Create an XS module using the Imager API
=head2 Foo.pm
=back
+=head1 Context objects
+
+Starting with Imager 0.93, Imager keeps some state per-thread rather
+than storing it in global (or static) variables. The intent is to
+improve support for multi-threaded perl programs.
+
+For the typical XS or Inline::C module using Imager's API this won't
+matter - the changes are hidden behind macros and rebuilding your
+module should require no source code changes.
+
+Some operations will be slightly slower, these include:
+
+=over
+
+=item *
+
+creating an image
+
+=item *
+
+reporting errors
+
+=item *
+
+creating I/O objects
+
+=item *
+
+setting/getting/testing image file limits
+
+=item *
+
+logging
+
+=back
+
+You can avoid this fairly minor overhead by adding a C<#define>:
+
+ #define IMAGER_NO_CONTEXT
+
+before including any Imager header files, but you will need to manage
+context objects yourself.
+
+Some functions and macros that are available without
+C<IMAGER_NO_CONTEXT> are not available with it defined, these are:
+
+=over
+
+=item *
+
+mm_log() - to avoid using a different context object for the line
+header and the line text you need to use im_log() instead, with a
+context object visible in scope.
+
+=back
+
+=head2 C<aIMCTX>
+
+With C<IMAGER_NO_CONTEXT> defined, C<aIMCTX> refers to the locally
+defined context object, either via one the of the C<dIMCTX> macros or
+as a parameter with the C<pIMCTX> macro.
+
+Without C<IMAGER_NO_CONTEXT>, C<aIMCTX> is a call to
+C<im_get_context()> which retrieves the context object for the current
+thread.
+
+There is no C<aIMCTX_> macro, any Imager function that can accept a
+context parameter always accepts it.
+
+=head2 C<pIMCTX>
+
+This macro declares a variable of type L</im_context_t> that's
+accessible via the C<aIMCTX> macro. This is intended for use as a
+parameter declaration for functions:
+
+ void f(pIMCTX) {
+ ... use aIMCTX here
+ }
+
+ void g(...) {
+ ...
+ f(aIMCTX);
+ }
+
+=head2 C<dIMCTX>
+
+Defines a local context variable and initializes it via
+L<im_get_context()|Imager::APIRef/im_get_context()>.
+
+=head2 C<dIMCTXim>
+
+Defines a local context variable and initializes it from the context
+stored in an L<image object|/i_img>, eg:
+
+ void f(i_img *im) {
+ dIMCTXim(im);
+ ...
+ }
+
+=head2 C<dIMCTXio>
+
+Defines a local context variable and initializes it from the context
+stored in an L<I/O object|/i_io_glue_t> object.
+
+ void f(i_io_glue_t *io) {
+ dIMCTXio(io);
+ ...
+ }
+
+=head2 C<dIMCTXctx>
+
+Defines a local context variable accessible via C<aIMCTX> in terms of
+an expression you supply:
+
+ void f(my_object *p) {
+ dIMCTXctx(p->context);
+ ...
+ }
+
+This can be used to define your own local context macro:
+
+ #define dIMCTXmine(mine) ((mine)->context)
+
+ void f(my_object *p) {
+ dIMCTXmine(p);
+ ...
+ }
+
+=head1 Mutex Functions
+
+Since some libraries are not thread safe, Imager's API includes some
+simple mutex functions.
+
+To create a mutex:
+
+ i_mutex_t m = i_mutex_new();
+
+To control or lock the mutex:
+
+ i_mutex_lock(m);
+
+To release or unlock the mutex:
+
+ i_mutex_unlock(m);
+
+To free any resources used by the mutex:
+
+ i_mutex_destroy(m);
+
+I most cases where you'd use these functions, your code would create
+the mutex in your BOOT section, then lock and unlock the mutex as
+needed to control access to the library.
+
+=head1 Context slots
+
+=for stopwords
+TLS APIs
+
+To avoid abstracting the platform TLS and thread clean up handling,
+Imager provides simple APIs for storing per-context information.
+
+To allocate a slot:
+
+ im_slot_t slot = im_context_slot_new(callback)
+
+where callback is a (possibly NULL) function pointer called when the
+context object is destroyed.
+
+By default, the stored value for a slot is NULL, whether for a new
+context or for a cloned context.
+
+To store a value:
+
+ im_context_slot_set(aIMCTX, slot, somevalue);
+
+where C<somevalue> can be represented as a C<void *>.
+
+To retrieve the value:
+
+ value = im_context_slot_get(aIMCTX, slot);
+
=head1 AUTHOR
Tony Cook <tonyc@cpan.org>
i_flood_cfill_border(im, 50, 50, fill, border);
# Error handling
+ im_clear_error(aIMCTX);
i_clear_error();
i_push_error(0, "Yep, it's broken");
i_push_error(errno, "Error writing");
+ im_push_error(aIMCTX, 0, "Something is wrong");
+ va_args args;
+ va_start(args, lastarg);
+ im_push_errorvf(ctx, code, format, args);
i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
+ im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno);
# Files
+ im_set_image_file_limits(aIMCTX, 500, 500, 1000000);
i_set_image_file_limits(500, 500, 1000000);
+ im_get_image_file_limits(aIMCTX, &width, &height, &bytes)
i_get_image_file_limits(&width, &height, &bytes)
- i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
+ im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t))
+ i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
# Fills
i_fill_t *fill = i_new_fill_solidf(&fcolor, combine);
# Image creation/destruction
i_img *img = i_sametype(src, width, height);
i_img *img = i_sametype_chans(src, width, height, channels);
+ i_img *img = im_img_16_new(aIMCTX, width, height, channels);
i_img *img = i_img_16_new(width, height, channels);
+ i_img *img = im_img_8_new(aIMCTX, width, height, channels);
i_img *img = i_img_8_new(width, height, channels);
+ i_img *img = im_img_double_new(aIMCTX, width, height, channels);
i_img *img = i_img_double_new(width, height, channels);
+ i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size)
i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
i_img_destroy(img)
# Image Implementation
+ i_img *im = im_img_alloc(aIMCTX);
+ i_img *im = i_img_alloc();
+ im_img_init(aIMCTX, im);
+ i_img_init(im);
# Image Information
// only channel 0 writable
# Logging
+ # Mutex functions
+ i_mutex_t m = i_mutex_new();
+ i_mutex_destroy(m);
+ i_mutex_lock(m);
+ i_mutex_unlock(m);
+
# Paletted images
# Tags
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
=over
-=item i_clear_error()
+=item i_push_errorf(int code, char const *fmt, ...)
+
+ i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
+
+A version of i_push_error() that does printf() like formatting.
+
+Does not support perl specific format codes.
+
+
+=for comment
+From: File error.c
+
+=item im_clear_error(ctx)
+X<im_clear_error API>X<i_clear_error API>
+ im_clear_error(aIMCTX);
i_clear_error();
Clears the error stack.
Called by any Imager function before doing any other processing.
+Also callable as C<i_clear_error()>.
+
=for comment
From: File error.c
-=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>
i_push_error(0, "Yep, it's broken");
i_push_error(errno, "Error writing");
+ im_push_error(aIMCTX, 0, "Something is wrong");
Called by an Imager function to push an error message onto the stack.
=for comment
From: File error.c
-=item i_push_errorf(int code, char const *fmt, ...)
+=item im_push_errorf(ctx, code, char const *fmt, ...)
- i_push_errorf(errno, "Cannot open file %s: %d", filename, errno);
+ im_push_errorf(aIMCTX, errno, "Cannot open file %s: %d", filename, errno);
-A version of i_push_error() that does printf() like formatting.
+A version of im_push_error() that does printf() like formatting.
Does not support perl specific format codes.
=for comment
From: File error.c
-=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>
+ va_args args;
+ va_start(args, lastarg);
+ im_push_errorvf(ctx, code, format, args);
Intended for use by higher level functions, takes a varargs pointer
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)>
+
=for comment
From: File error.c
=for comment
From: File image.c
-=item i_get_image_file_limits(&width, &height, &bytes)
-
+=item im_get_image_file_limits(ctx, &width, &height, &bytes)
+X<im_get_image_file_limits API>X<i_get_image_file_limits>
+ im_get_image_file_limits(aIMCTX, &width, &height, &bytes)
i_get_image_file_limits(&width, &height, &bytes)
Retrieves the file limits set by i_set_image_file_limits().
=back
+Also callable as C<i_get_image_file_limits(&width, &height, &bytes)>.
+
=for comment
From: File limits.c
-=item i_int_check_image_file_limits(width, height, channels, sample_size)
-
+=item im_int_check_image_file_limits(width, height, channels, sample_size)
+X<im_int_check_image_file_limits API>X<i_int_check_image_file_limits>
- i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
+ im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t))
+ i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
Checks the size of a file in memory against the configured image file
limits.
This function is intended to be called by image file read functions.
+Also callable as C<i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)>.
+
=for comment
From: File limits.c
-=item i_set_image_file_limits(width, height, bytes)
-
+=item im_set_image_file_limits(ctx, width, height, bytes)
+X<im_set_image_file_limits API>X<i_set_image_file_limits API>
+ im_set_image_file_limits(aIMCTX, 500, 500, 1000000);
i_set_image_file_limits(500, 500, 1000000);
Set limits on the sizes of images read by Imager.
Returns non-zero on success.
+Also callable as C<i_set_image_file_limits(width, height, bytes)>.
+
=for comment
From: File limits.c
=over
-=item io_new_bufchain()
+=item im_io_new_bufchain(ctx)
+X<im_io_new_bufchain API>X<i_io_new_bufchain API>
-returns a new io_glue object that has the 'empty' source and but can
+Returns a new io_glue object that has the 'empty' source and but can
be written to and read from later (like a pseudo file).
+Also callable as C<io_new_bufchain()>.
+
=for comment
From: File iolayer.c
-=item io_new_buffer(data, length)
+=item im_io_new_buffer(ctx, data, length)
+X<im_io_new_buffer API>X<io_new_buffer API>
Returns a new io_glue object that has the source defined as reading
from specified buffer. Note that the buffer is not copied.
+ ctx - an Imager context object
data - buffer to read from
length - length of buffer
+Also callable as C<io_new_buffer(data, length>.
+
=for comment
From: File iolayer.c
-=item io_new_cb(p, read_cb, write_cb, seek_cb, close_cb, destroy_cb)
+=item im_io_new_cb(ctx, p, read_cb, write_cb, seek_cb, close_cb, destroy_cb)
+X<im_io_new_cb API>X<io_new_cb API>
Create a new I/O layer object that calls your supplied callbacks.
=back
+Also callable as C<io_new_cb(p, readcb, writecb, seekcb, closecb,
+destroycb)>.
+
=for comment
From: File iolayer.c
-=item io_new_fd(fd)
+=item im_io_new_fd(ctx, file)
+X<io_new_fd API>X<im_io_new_fd API>
-returns a new io_glue object that has the source defined as reading
+Returns a new io_glue object that has the source defined as reading
from specified file descriptor. Note that the the interface to receiving
data from the io_glue callbacks hasn't been done yet.
- fd - file descriptor to read/write from
+ ctx - and Imager context object
+ file - file descriptor to read/write from
+
+Also callable as C<io_new_fd(file)>.
=for comment
From: File iolayer.c
=item io_slurp(ig, c)
+X<io_slurp API>
Takes the source that the io_glue is bound to and allocates space for
a return buffer and returns the entire content in a single buffer.
From: File iolayer.c
=item io_glue_destroy(ig)
+X<io_glue_destroy API>
io_glue_destroy(ig);
=over
-=item i_img_16_new(x, y, ch)
+=item i_sametype(C<im>, C<xsize>, C<ysize>)
- i_img *img = i_img_16_new(width, height, channels);
+ i_img *img = i_sametype(src, width, height);
-Create a new 16-bit/sample image.
+Returns an image of the same type (sample size, channels, paletted/direct).
-Returns the image on success, or NULL on failure.
+For paletted images the palette is copied from the source.
=for comment
-From: File img16.c
+From: File image.c
-=item i_img_8_new(x, y, ch)
+=item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<channels>)
+ i_img *img = i_sametype_chans(src, width, height, channels);
- i_img *img = i_img_8_new(width, height, channels);
+Returns an image of the same type (sample size).
-Creates a new image object I<x> pixels wide, and I<y> pixels high with
-I<ch> channels.
+For paletted images the equivalent direct type is returned.
=for comment
-From: File img8.c
-
-=item i_img_double_new(i_img_dim x, i_img_dim y, int ch)
+From: File image.c
- i_img *img = i_img_double_new(width, height, channels);
+=item im_img_16_new(ctx, x, y, ch)
+X<im_img_16_new API>X<i_img_16_new API>
-Creates a new double per sample image.
+ i_img *img = im_img_16_new(aIMCTX, width, height, channels);
+ i_img *img = i_img_16_new(width, height, channels);
+Create a new 16-bit/sample image.
-=for comment
-From: File imgdouble.c
+Returns the image on success, or NULL on failure.
-=item i_img_pal_new(C<x>, C<y>, C<channels>, C<maxpal>)
+Also callable as C<i_img_16_new(x, y, ch)>
- i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
+=for comment
+From: File img16.c
-Creates a new paletted image of the supplied dimensions.
+=item im_img_8_new(ctx, x, y, ch)
+X<im_img_8_new API>X<i_img_8_new API>
-C<maxpal> is the maximum palette size and should normally be 256.
+ i_img *img = im_img_8_new(aIMCTX, width, height, channels);
+ i_img *img = i_img_8_new(width, height, channels);
-Returns a new image or NULL on failure.
+Creates a new image object I<x> pixels wide, and I<y> pixels high with
+I<ch> channels.
=for comment
-From: File palimg.c
-
-=item i_sametype(C<im>, C<xsize>, C<ysize>)
+From: File img8.c
+=item im_img_double_new(ctx, x, y, ch)
+X<im_img_double_new API>X<i_img_double_new API>
- i_img *img = i_sametype(src, width, height);
+ i_img *img = im_img_double_new(aIMCTX, width, height, channels);
+ i_img *img = i_img_double_new(width, height, channels);
-Returns an image of the same type (sample size, channels, paletted/direct).
+Creates a new double per sample image.
-For paletted images the palette is copied from the source.
+Also callable as C<i_img_double_new(width, height, channels)>.
=for comment
-From: File image.c
+From: File imgdouble.c
-=item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<channels>)
+=item im_img_pal_new(ctx, C<x>, C<y>, C<channels>, C<maxpal>)
+X<im_img_pal_new API>X<i_img_pal_new API>
+ i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size)
+ i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
- i_img *img = i_sametype_chans(src, width, height, channels);
+Creates a new paletted image of the supplied dimensions.
-Returns an image of the same type (sample size).
+C<maxpal> is the maximum palette size and should normally be 256.
-For paletted images the equivalent direct type is returned.
+Returns a new image or NULL on failure.
+
+Also callable as C<i_img_pal_new(width, height, channels, max_palette_size)>.
=for comment
-From: File image.c
+From: File palimg.c
=item i_img_destroy(C<img>)
=over
-=item i_img_alloc()
+=item im_img_alloc(aIMCTX)
+X<im_img_alloc API>X<i_img_alloc API>
+
+ i_img *im = im_img_alloc(aIMCTX);
+ i_img *im = i_img_alloc();
Allocates a new i_img structure.
=for comment
From: File image.c
-=item i_img_init(C<img>)
+=item im_img_init(aIMCTX, image)
+X<im_img_init API>X<i_img_init API>
+
+ im_img_init(aIMCTX, im);
+ 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.
=for comment
=for comment
From: File log.c
-=item mm_log((level, format, ...))
-This is the main entry point to logging. Note that the extra set of
-parentheses are required due to limitations in C89 macros.
+=back
+
+=head2 Mutex functions
+
+=over
+
+=item i_mutex_new()
+
+ i_mutex_t m = i_mutex_new();
+
+Create a mutex.
+
+If a critical section cannot be created for whatever reason, Imager
+will abort.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_destroy(m)
+
+ i_mutex_destroy(m);
+
+Destroy a mutex.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_lock(m)
+
+ i_mutex_lock(m);
+
+Lock the mutex, waiting if another thread has the mutex locked.
+
+
+=for comment
+From: File mutexwin.c
+
+=item i_mutex_unlock(m)
+
+ i_mutex_unlock(m);
-This will format a string with the current file and line number to the
-log file if logging is enabled.
+Release the mutex.
+
+The behavior of releasing a mutex you don't hold is unspecified.
=for comment
-From: File log.h
+From: File mutexwin.c
=back
=for comment
From: File io.c
+=item im_context_refdec(ctx, where)
+X<im_context_refdec API>
+=section Context objects
+
+ im_context_refdec(aIMCTX, "a description");
+
+Remove a reference to the context, releasing it if all references have
+been removed.
+
+
+=for comment
+From: File context.c
+
+=item im_context_refinc(ctx, where)
+X<im_context_refinc API>
+=section Context objects
+
+ im_context_refinc(aIMCTX, "a description");
+
+Add a new reference to the context.
+
+
+=for comment
+From: File context.c
+
+=item im_context_slot_get(ctx, slot)
+
+Retrieve the value previously stored in the given slot of the context
+object.
+
+
+=for comment
+From: File context.c
+
+=item im_context_slot_new(destructor)
+
+Allocate a new context-local-storage slot.
+
+
+=for comment
+From: File context.c
+
+=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.
+
+
+=for comment
+From: File context.c
+
+=item im_errors(ctx)
+
+ i_errmsg *errors = im_errors(aIMCTX);
+ 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()>.
+
+
+=for comment
+From: File error.c
+
+=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.
+
+
+=for comment
+From: File imext.c
+
+
+
+=back
+
+
+=head1 UNDOCUMENTED
+
+The following API functions are undocumented so far, hopefully this
+will change:
+
+=over
+
+=item *
+
+B<im_lhead>
+
+=item *
+
+B<im_loog>
+
+=item *
+
+B<mm_log>
+
=back
=head1 SEE ALSO
-Imager, Imager::ExtUtils, Imager::Inline
+Imager, Imager::API, Imager::ExtUtils, Imager::Inline
=cut
--- /dev/null
+=head1 NAME
+
+Imager::Threads - Imager and threads
+
+=head1 SYNOPSIS
+
+ use Imager;
+ use threads;
+ Imager->preload;
+
+ threads->create(...);
+
+=head1 DESCRIPTION
+
+Starting from version 0.93 Imager attempts to work safely with perl's
+C<ithreads>.
+
+Previous versions stored some state in global variables, in particular
+the internal error stack.
+
+However there are some limitations:
+
+=over
+
+=item *
+
+Imager's debug malloc isn't thread safe and will never be. Imager's
+debug malloc is disabled by default.
+
+=item *
+
+C<libtiff>, which Imager uses for TIFF file support is not thread
+safe, C<Imager::File::TIFF> works around this by single-threading its
+access to C<libtiff>.
+
+=item *
+
+C<giflib>, which Imager uses for GIF support is not thread safe before
+version 5. C<Imager::File::GIF> works around this by single threading
+its access to C<giflib>.
+
+=item *
+
+C<T1Lib>, used by one of Imager's font drivers, is not thread safe.
+C<Imager::Font::T1> works around this by single threading access.
+
+=item *
+
+killing a thread reading or writing TIFF or GIF files, or using T1
+fonts through C<Imager::Font::T1> may deadlock other threads when they
+attempt to read or write TIFF or GIF files, or work with Type 1 fonts.
+
+=item *
+
+Fill, font, color or I/O layer objects created in one thread and are
+valid for use in child threads. If you manage to duplicate such an
+object in another thread, you get to keep both pieces when it breaks.
+
+=back
+
+Note that if you have another module using C<libtiff> or C<giflib> it
+may interact with Imager's use of those libraries in a threaded
+environment, since there's no way to co-ordinate access to the global
+information C<libtiff> and C<giflib> maintain.
+
+Imager currently doesn't use threads itself.
+
+=head1 SEE ALSO
+
+Imager, C<threads>
+
+=head1 AUTHOR
+
+Tony Cook <tony@cpan.org>
+
+=cut
*/
+#define IMAGER_NO_CONTEXT
#include "imageri.h"
-#define DEF_BYTES_LIMIT 0x40000000
-
-static i_img_dim max_width, max_height;
-static size_t max_bytes = DEF_BYTES_LIMIT;
-
/*
-=item i_set_image_file_limits(width, height, bytes)
-
+=item im_set_image_file_limits(ctx, width, height, bytes)
+X<im_set_image_file_limits API>X<i_set_image_file_limits API>
=category Files
+=synopsis im_set_image_file_limits(aIMCTX, 500, 500, 1000000);
=synopsis i_set_image_file_limits(500, 500, 1000000);
Set limits on the sizes of images read by Imager.
Returns non-zero on success.
+Also callable as C<i_set_image_file_limits(width, height, bytes)>.
+
=cut
*/
int
-i_set_image_file_limits(i_img_dim width, i_img_dim height, size_t bytes) {
+im_set_image_file_limits(pIMCTX, i_img_dim width, i_img_dim height, size_t bytes) {
i_clear_error();
if (width < 0) {
return 0;
}
- max_width = width;
- max_height = height;
- max_bytes = bytes ? bytes : DEF_BYTES_LIMIT;
+ aIMCTX->max_width = width;
+ aIMCTX->max_height = height;
+ aIMCTX->max_bytes = bytes ? bytes : DEF_BYTES_LIMIT;
return 1;
}
/*
-=item i_get_image_file_limits(&width, &height, &bytes)
-
+=item im_get_image_file_limits(ctx, &width, &height, &bytes)
+X<im_get_image_file_limits API>X<i_get_image_file_limits>
=category Files
+=synopsis im_get_image_file_limits(aIMCTX, &width, &height, &bytes)
=synopsis i_get_image_file_limits(&width, &height, &bytes)
Retrieves the file limits set by i_set_image_file_limits().
=back
+Also callable as C<i_get_image_file_limits(&width, &height, &bytes)>.
+
=cut
*/
int
-i_get_image_file_limits(i_img_dim *width, i_img_dim *height, size_t *bytes) {
- i_clear_error();
+im_get_image_file_limits(pIMCTX, i_img_dim *width, i_img_dim *height, size_t *bytes) {
+ im_clear_error(aIMCTX);
- *width = max_width;
- *height = max_height;
- *bytes = max_bytes;
+ *width = aIMCTX->max_width;
+ *height = aIMCTX->max_height;
+ *bytes = aIMCTX->max_bytes;
return 1;
}
/*
-=item i_int_check_image_file_limits(width, height, channels, sample_size)
-
+=item im_int_check_image_file_limits(width, height, channels, sample_size)
+X<im_int_check_image_file_limits API>X<i_int_check_image_file_limits>
=category Files
-=synopsis i_i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
+=synopsis im_int_check_image_file_limits(aIMCTX, width, height, channels, sizeof(i_sample_t))
+=synopsis i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t))
Checks the size of a file in memory against the configured image file
limits.
This function is intended to be called by image file read functions.
+Also callable as C<i_int_check_image_file_limits(width, height, channels, sizeof(i_sample_t)>.
+
=cut
*/
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(pIMCTX, i_img_dim width, i_img_dim height, int channels, size_t sample_size) {
size_t bytes;
- i_clear_error();
+ im_clear_error(aIMCTX);
if (width <= 0) {
- i_push_errorf(0, "file size limit - image width of %" i_DF " is not positive",
+ im_push_errorf(aIMCTX, 0, "file size limit - image width of %" i_DF " is not positive",
i_DFc(width));
return 0;
}
- if (max_width && width > max_width) {
- i_push_errorf(0, "file size limit - image width of %" i_DF " exceeds limit of %" i_DF,
- i_DFc(width), i_DFc(max_width));
+ if (aIMCTX->max_width && width > aIMCTX->max_width) {
+ im_push_errorf(aIMCTX, 0, "file size limit - image width of %" i_DF " exceeds limit of %" i_DF,
+ i_DFc(width), i_DFc(aIMCTX->max_width));
return 0;
}
if (height <= 0) {
- i_push_errorf(0, "file size limit - image height of %" i_DF " is not positive",
+ im_push_errorf(aIMCTX, 0, "file size limit - image height of %" i_DF " is not positive",
i_DFc(height));
return 0;
}
- if (max_height && height > max_height) {
- i_push_errorf(0, "file size limit - image height of %" i_DF
- " exceeds limit of %" i_DF, i_DFc(height), i_DFc(max_height));
+ if (aIMCTX->max_height && height > aIMCTX->max_height) {
+ im_push_errorf(aIMCTX, 0, "file size limit - image height of %" i_DF
+ " exceeds limit of %" i_DF, i_DFc(height), i_DFc(aIMCTX->max_height));
return 0;
}
if (channels < 1 || channels > MAXCHANNELS) {
- i_push_errorf(0, "file size limit - channels %d out of range",
+ im_push_errorf(aIMCTX, 0, "file size limit - channels %d out of range",
channels);
return 0;
}
if (sample_size < 1 || sample_size > sizeof(long double)) {
- i_push_errorf(0, "file size limit - sample_size %ld out of range",
+ im_push_errorf(aIMCTX, 0, "file size limit - sample_size %ld out of range",
(long)sample_size);
return 0;
}
bytes = width * height * channels * sample_size;
if (bytes / width != height * channels * sample_size
|| bytes / height != width * channels * sample_size) {
- i_push_error(0, "file size limit - integer overflow calculating storage");
+ im_push_error(aIMCTX, 0, "file size limit - integer overflow calculating storage");
return 0;
}
- if (max_bytes) {
- if (bytes > max_bytes) {
- i_push_errorf(0, "file size limit - storage size of %lu "
+ if (aIMCTX->max_bytes) {
+ if (bytes > aIMCTX->max_bytes) {
+ im_push_errorf(aIMCTX, 0, "file size limit - storage size of %lu "
"exceeds limit of %lu", (unsigned long)bytes,
- (unsigned long)max_bytes);
+ (unsigned long)aIMCTX->max_bytes);
return 0;
}
}
+#define IMAGER_NO_CONTEXT
+#include "imageri.h"
#include "imconfig.h"
#include "log.h"
#include <stdlib.h>
#define DTBUFF 50
#define DATABUFF DTBUFF+3+10+1+5+1+1
+#if 0
static int log_level = 0;
static FILE *lg_file = NULL;
-static char *date_format = "%Y/%m/%d %H:%M:%S";
static char date_buffer[DTBUFF];
static char data_buffer[DATABUFF];
+#endif
+
+#define LOG_DATE_FORMAT "%Y/%m/%d %H:%M:%S"
+static void
+im_vloog(pIMCTX, int level, const char *fmt, va_list ap);
/*
* Logging is active
*/
int
-i_init_log(const char* name,int level) {
+im_init_log(pIMCTX, const char* name,int level) {
i_clear_error();
- log_level = level;
+ aIMCTX->log_level = level;
if (level < 0) {
- lg_file = NULL;
+ aIMCTX->lg_file = NULL;
} else {
if (name == NULL) {
- lg_file = stderr;
+ aIMCTX->lg_file = stderr;
} else {
- if (NULL == (lg_file = fopen(name, "w+")) ) {
- i_push_errorf(errno, "Cannot open file '%s': (%d)", name, errno);
+ if (NULL == (aIMCTX->lg_file = fopen(name, "w+")) ) {
+ im_push_errorf(aIMCTX, errno, "Cannot open file '%s': (%d)", name, errno);
return 0;
}
}
}
- if (lg_file) {
- setvbuf(lg_file, NULL, _IONBF, BUFSIZ);
- mm_log((0,"Imager - log started (level = %d)\n", level));
+ if (aIMCTX->lg_file) {
+ setvbuf(aIMCTX->lg_file, NULL, _IONBF, BUFSIZ);
+ im_log((aIMCTX, 0,"Imager - log started (level = %d)\n", level));
}
- return lg_file != NULL;
+ return aIMCTX->lg_file != NULL;
}
void
i_fatal(int exitcode,const char *fmt, ... ) {
va_list ap;
- time_t timi;
- struct tm *str_tm;
-
- if (lg_file != NULL) {
- timi = time(NULL);
- str_tm = localtime(&timi);
- if ( strftime(date_buffer, DTBUFF, date_format, str_tm) )
- fprintf(lg_file,"[%s] ",date_buffer);
+ dIMCTX;
+
+ if (aIMCTX->lg_file != NULL) {
va_start(ap,fmt);
- vfprintf(lg_file,fmt,ap);
+ im_vloog(aIMCTX, 0, fmt, ap);
va_end(ap);
}
exit(exitcode);
}
+void
+im_fatal(pIMCTX, int exitcode,const char *fmt, ... ) {
+ va_list ap;
+
+ if (aIMCTX->lg_file != NULL) {
+ va_start(ap,fmt);
+ im_vloog(aIMCTX, 0, fmt, ap);
+ va_end(ap);
+ }
+ exit(exitcode);
+}
/*
=item i_loog(level, format, ...)
=cut
*/
+static void
+im_vloog(pIMCTX, int level, const char *fmt, va_list ap) {
+ time_t timi;
+ struct tm *str_tm;
+ char date_buffer[DTBUFF];
+
+ if (!aIMCTX->lg_file || level > aIMCTX->log_level)
+ return;
+
+ timi = time(NULL);
+ str_tm = localtime(&timi);
+ strftime(date_buffer, DTBUFF, LOG_DATE_FORMAT, str_tm);
+ fprintf(aIMCTX->lg_file, "[%s] %10s:%-5d %3d: ", date_buffer,
+ aIMCTX->filename, aIMCTX->line, level);
+ vfprintf(aIMCTX->lg_file, fmt, ap);
+ fflush(aIMCTX->lg_file);
+}
+
void
i_loog(int level,const char *fmt, ... ) {
+ dIMCTX;
va_list ap;
- if (level > log_level) return;
- if (lg_file != NULL) {
- fputs(data_buffer, lg_file);
- fprintf(lg_file, "%3d: ",level);
- va_start(ap,fmt);
- vfprintf(lg_file, fmt, ap);
- fflush(lg_file);
- va_end(ap);
- }
+
+ if (!aIMCTX->lg_file || level > aIMCTX->log_level)
+ return;
+
+ va_start(ap,fmt);
+ im_vloog(aIMCTX, level, fmt, ap);
+ va_end(ap);
+}
+
+void
+im_loog(pIMCTX, int level,const char *fmt, ... ) {
+ va_list ap;
+
+ if (!aIMCTX->lg_file || level > aIMCTX->log_level)
+ return;
+
+ va_start(ap,fmt);
+ im_vloog(aIMCTX, level, fmt, ap);
+ va_end(ap);
}
/*
*/
void
-i_lhead(const char *file, int line) {
- time_t timi;
- struct tm *str_tm;
-
- if (lg_file != NULL) {
- timi = time(NULL);
- str_tm = localtime(&timi);
- strftime(date_buffer, DTBUFF, date_format, str_tm);
-#ifdef IMAGER_SNPRINTF
- snprintf(data_buffer, sizeof(data_buffer), "[%s] %10s:%-5d ", date_buffer, file, line);
-#else
- sprintf(data_buffer, "[%s] %10s:%-5d ", date_buffer, file, line);
-#endif
+im_lhead(pIMCTX, const char *file, int line) {
+ if (aIMCTX->lg_file != NULL) {
+ aIMCTX->filename = file;
+ aIMCTX->line = line;
}
}
+void i_lhead(const char *file, int line) {
+ dIMCTX;
+
+ im_lhead(aIMCTX, file, line);
+}
+
#else
/*
}
void i_fatal(int exitcode,const char *fmt, ... ) { exit(exitcode); }
+void im_fatal(pIMCTX, int exitcode,const char *fmt, ... ) { exit(exitcode); }
void
i_loog(int level,const char *fmt, ... ) {
global: creates a global variable FILE* lg_file
*/
-int i_init_log( const char *name, int onoff );
+int im_init_log(pIMCTX, const char *name, int onoff );
+#define i_init_log(name, onoff) im_init_log(aIMCTX, name, onoff)
+#ifndef IMAGER_NO_CONTEXT
void i_fatal ( int exitcode,const char *fmt, ... );
+#endif
+void im_fatal (pIMCTX, int exitcode,const char *fmt, ... );
+void im_lhead ( pIMCTX, const char *file, int line );
void i_lhead ( const char *file, int line );
void i_loog(int level,const char *msg, ... ) I_FORMAT_ATTR(2,3);
+void im_loog(pIMCTX, int level,const char *msg, ... ) I_FORMAT_ATTR(3,4);
/*
-=item mm_log((level, format, ...))
+=item im_log((aIMCTX, level, format, ...))
=category Logging
This is the main entry point to logging. Note that the extra set of
This will format a string with the current file and line number to the
log file if logging is enabled.
+This must be called with a context object defined by one of the
+C<dIMCTX> macros in scope.
+
+This can also be called as C<mm_log((level, format, args))> in which
+case the currently active context is used and any in scope context is
+ignored.
+
=cut
*/
#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)
+#define im_log(x)
#endif
=cut
*/
+#define IMAGER_NO_CONTEXT
+
#include "imager.h"
#include "imageri.h"
=cut
*/
-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) {
+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) {
i_img *im;
i_img_mask_ext *ext;
+ dIMCTXim(targ);
- i_clear_error();
+ im_clear_error(aIMCTX);
if (x >= targ->xsize || y >= targ->ysize) {
- i_push_error(0, "subset outside of target image");
+ im_push_error(aIMCTX, 0, "subset outside of target image");
return NULL;
}
if (mask) {
if (y+h > targ->ysize)
h = targ->ysize - y;
- im = mymalloc(sizeof(i_img));
+ im = im_img_alloc(aIMCTX);
+
memcpy(im, &IIM_base_masked, sizeof(i_img));
+ i_tags_new(&im->tags);
im->xsize = w;
im->ysize = h;
im->channels = targ->channels;
ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize);
im->ext_data = ext;
+ im_img_init(aIMCTX, im);
+
return im;
}
return result;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
return result;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
--- /dev/null
+/*
+ dummy mutexes, for non-threaded builds
+*/
+
+#include "imageri.h"
+
+#include <pthread.h>
+
+/* documented in mutexwin.c */
+
+struct i_mutex_tag {
+ int dummy;
+};
+
+i_mutex_t
+i_mutex_new(void) {
+ i_mutex_t m;
+
+ if (!m)
+ i_fatal(3, "Cannot allocate mutex object");
+ m = malloc(sizeof(*m));
+
+ return m;
+}
+
+void
+i_mutex_destroy(i_mutex_t m) {
+ free(m);
+}
+
+void
+i_mutex_lock(i_mutex_t m) {
+ (void)m;
+}
+
+void
+i_mutex_unlock(i_mutex_t m) {
+ (void)m;
+}
--- /dev/null
+/*
+ pthreads mutexes
+*/
+
+#include "imageri.h"
+
+#include <pthread.h>
+#include <errno.h>
+
+/* documented in mutexwin.c */
+
+struct i_mutex_tag {
+ pthread_mutex_t mutex;
+};
+
+i_mutex_t
+i_mutex_new(void) {
+ i_mutex_t m;
+
+ m = malloc(sizeof(*m));
+ if (!m)
+ i_fatal(3, "Cannot allocate mutex object");
+ if (pthread_mutex_init(&m->mutex, NULL) != 0) {
+ i_fatal(3, "Error initializing mutex %d", errno);
+ }
+
+ return m;
+}
+
+void
+i_mutex_destroy(i_mutex_t m) {
+ pthread_mutex_destroy(&(m->mutex));
+ free(m);
+}
+
+void
+i_mutex_lock(i_mutex_t m) {
+ pthread_mutex_lock(&(m->mutex));
+}
+
+void
+i_mutex_unlock(i_mutex_t m) {
+ pthread_mutex_unlock(&m->mutex);
+}
--- /dev/null
+/*
+=head1 NAME
+
+mutex.c - Imager's mutex API.
+
+=head1 FUNCTIONS
+
+=over
+
+=cut
+*/
+
+#include "imageri.h"
+
+#include <windows.h>
+
+struct i_mutex_tag {
+ CRITICAL_SECTION section;
+};
+
+/*
+=item i_mutex_new()
+=category Mutex functions
+=synopsis i_mutex_t m = i_mutex_new();
+=order 10
+
+Create a mutex.
+
+If a critical section cannot be created for whatever reason, Imager
+will abort.
+
+=cut
+*/
+
+i_mutex_t
+i_mutex_new(void) {
+ i_mutex_t m;
+
+ m = malloc(sizeof(*m));
+ if (!m)
+ i_fatal(3, "Cannot allocate mutex object");
+ InitializeCriticalSection(&(m->section));
+
+ return m;
+}
+
+/*
+=item i_mutex_destroy(m)
+=category Mutex functions
+=synopsis i_mutex_destroy(m);
+
+Destroy a mutex.
+
+=cut
+*/
+
+void
+i_mutex_destroy(i_mutex_t m) {
+ DeleteCriticalSection(&(m->section));
+ free(m);
+}
+
+/*
+=item i_mutex_lock(m)
+=category Mutex functions
+=synopsis i_mutex_lock(m);
+
+Lock the mutex, waiting if another thread has the mutex locked.
+
+=cut
+*/
+
+void
+i_mutex_lock(i_mutex_t m) {
+ EnterCriticalSection(&(m->section));
+}
+
+/*
+=item i_mutex_unlock(m)
+=category Mutex functions
+=synopsis i_mutex_unlock(m);
+
+Release the mutex.
+
+The behavior of releasing a mutex you don't hold is unspecified.
+
+=cut
+*/
+
+void
+i_mutex_unlock(i_mutex_t m) {
+ LeaveCriticalSection(&(m->section));
+}
+
=cut
*/
+#define IMAGER_NO_CONTEXT
+
#include "imager.h"
#include "imageri.h"
};
/*
-=item i_img_pal_new(C<x>, C<y>, C<channels>, C<maxpal>)
-
+=item im_img_pal_new(ctx, C<x>, C<y>, C<channels>, C<maxpal>)
+X<im_img_pal_new API>X<i_img_pal_new API>
=category Image creation/destruction
+=synopsis i_img *img = im_img_pal_new(aIMCTX, width, height, channels, max_palette_size)
=synopsis i_img *img = i_img_pal_new(width, height, channels, max_palette_size)
Creates a new paletted image of the supplied dimensions.
Returns a new image or NULL on failure.
+Also callable as C<i_img_pal_new(width, height, channels, max_palette_size)>.
+
=cut
*/
i_img *
-i_img_pal_new(i_img_dim x, i_img_dim y, int channels, int maxpal) {
+im_img_pal_new(pIMCTX, i_img_dim x, i_img_dim y, int channels, int maxpal) {
i_img *im;
i_img_pal_ext *palext;
size_t bytes, line_bytes;
return NULL;
}
if (channels < 1 || channels > MAXCHANNELS) {
- i_push_errorf(0, "Channels must be positive and <= %d", MAXCHANNELS);
+ im_push_errorf(aIMCTX, 0, "Channels must be positive and <= %d", MAXCHANNELS);
return NULL;
}
bytes = sizeof(i_palidx) * x * y;
=cut
*/
-int i_img_to_rgb_inplace(i_img *im) {
+int
+i_img_to_rgb_inplace(i_img *im) {
i_img temp;
+ dIMCTXim(im);
if (im->virtual)
return 0;
i_img *i_img_to_pal(i_img *src, i_quantize *quant) {
i_palidx *result;
i_img *im;
+ dIMCTXim(src);
i_clear_error();
=cut
*/
-i_img *i_img_to_rgb(i_img *src) {
+i_img *
+i_img_to_rgb(i_img *src) {
+ dIMCTXim(src);
i_img *im = i_img_empty_ch(NULL, src->xsize, src->ysize, src->channels);
i_img_rgb_convert(im, src);
return 0;
}
else {
- mm_log((1, "i_ppix: color(%d,%d,%d) not found, converting to rgb\n",
+ dIMCTXim(im);
+ im_log((aIMCTX, 1, "i_ppix: color(%d,%d,%d) not found, converting to rgb\n",
val->channel[0], val->channel[1], val->channel[2]));
if (i_img_to_rgb_inplace(im)) {
return i_ppix(im, x, y, val);
if (chans) {
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]);
}
}
}
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;
}
/* 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;
}
}
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
/* 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;
}
}
}
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;
}
return count;
}
else {
+ dIMCTXim(im);
i_push_error(0, "Image position outside of image");
return -1;
}
-#include "imager.h"
+#include "imdatatypes.h"
+#include "immacros.h"
/* structures for passing data between Imager-plugin and the Imager-module */
#define i_color_set(cl,r,g,b,a) (symbol_table->i_color_set(cl,r,g,b,a))
#define i_color_info(cl) (symbol_table->i_color_info(cl))
-#define i_img_new() (symbol_table->i_img_new())
-#define i_img_empty(im,x,y) ((symbol_table->i_img_empty(im,x,y))
-#define i_img_empty_ch(im,x,y,ch) ((symbol_table->i_img_empty_ch(im,x,y,ch))
-#define i_img_exorcise(im) (symbol_table->i_img_exorcise(im))
-#define i_img_info(im,info) (symbol_table->i_img_info(im,info))
+#define im_get_context() (symbol_table->im_get_context_f())
+#define i_img_empty_ch(im,x,y,ch) ((symbol_table->i_img_empty_ch_f(im_get_context(), im,x,y,ch))
+#define i_img_exorcise(im) (symbol_table->i_img_exorcise_f(im))
+#define i_img_info(im,info) (symbol_table->i_img_info_f(im,info))
-#define i_img_setmask(im,ch_mask) (symbol_table->i_img_setmask(im,ch_mask))
-#define i_img_getmask(im) (symbol_table->i_img_getmask(im))
+#define i_img_setmask(im,ch_mask) (symbol_table->i_img_setmask_f(im,ch_mask))
+#define i_img_getmask(im) (symbol_table->i_img_getmask_f(im))
/*
Not needed? The i_gpix() macro in image.h will call the right function
-d "testout" or mkdir "testout";
-plan tests => 115;
+plan tests => 117;
require Inline;
Inline->import(with => 'Imager');
Inline->import("FORCE"); # force rebuild
return i_psampf(im, 0, 1, 0, samps, NULL, chan_count);
}
+int
+test_mutex() {
+ i_mutex_t m;
+
+ m = i_mutex_new();
+ i_mutex_lock(m);
+ i_mutex_unlock(m);
+ i_mutex_destroy(m);
+}
+
+int
+test_slots() {
+ im_slot_t slot = im_context_slot_new(NULL);
+
+ if (im_context_slot_get(aIMCTX, slot)) {
+ fprintf(stderr, "slots should default to NULL\n");
+ return 0;
+ }
+ if (!im_context_slot_set(aIMCTX, slot, &slot)) {
+ fprintf(stderr, "set slot failed\n");
+ return 0;
+ }
+
+ if (im_context_slot_get(aIMCTX, slot) != &slot) {
+ fprintf(stderr, "get slot didn't match\n");
+ return 0;
+ }
+
+ return 1;
+}
+
EOS
my $im = Imager->new(xsize=>50, ysize=>50);
is($im->type, "paletted", "make sure we kept the image type");
}
+ok(test_mutex(), "call mutex APIs");
+
+ok(test_slots(), "call slot APIs");
+
sub _get_error {
my @errors = Imager::i_errors();
return join(": ", map $_->[0], @errors);
--- /dev/null
+#!perl -w
+#
+# this tests both the Inline interface and the API with IMAGER_NO_CONTEXT
+use strict;
+use Test::More;
+use Imager::Test qw(is_color3 is_color4);
+eval "require Inline::C;";
+plan skip_all => "Inline required for testing API" if $@;
+
+eval "require Parse::RecDescent;";
+plan skip_all => "Could not load Parse::RecDescent" if $@;
+
+use Cwd 'getcwd';
+plan skip_all => "Inline won't work in directories with spaces"
+ if getcwd() =~ / /;
+
+plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
+ if $] =~ /^5\.005_0[45]$/;
+
+-d "testout" or mkdir "testout";
+
+plan tests => 5;
+require Inline;
+Inline->import(C => Config => AUTO_INCLUDE => "#define IMAGER_NO_CONTEXT\n");
+Inline->import(with => 'Imager');
+Inline->import("FORCE"); # force rebuild
+#Inline->import(C => Config => OPTIMIZE => "-g");
+
+Inline->bind(C => <<'EOS');
+#include <math.h>
+
+Imager make_10x10() {
+ dIMCTX;
+ i_img *im = i_img_8_new(10, 10, 3);
+ i_color c;
+ c.channel[0] = c.channel[1] = c.channel[2] = 255;
+ i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
+
+ return im;
+}
+
+void error_dIMCTX() {
+ dIMCTX;
+ im_clear_error(aIMCTX);
+ im_push_error(aIMCTX, 0, "test1");
+ im_push_errorf(aIMCTX, 0, "test%d", 2);
+
+ im_log((aIMCTX, 0, "test logging\n"));
+}
+
+void error_dIMCTXim(Imager im) {
+ dIMCTXim(im);
+ im_clear_error(aIMCTX);
+ im_push_error(aIMCTX, 0, "test1");
+}
+
+int context_refs() {
+ dIMCTX;
+
+ im_context_refinc(aIMCTX, "context_refs");
+ im_context_refdec(aIMCTX, "context_refs");
+
+ return 1;
+}
+
+EOS
+
+Imager->open_log(log => "testout/t84inlinectx.log");
+
+my $im2 = make_10x10();
+ok($im2, "make an image");
+is_color3($im2->getpixel(x => 0, y => 0), 255, 255, 255,
+ "check the colors");
+error_dIMCTX();
+is(_get_error(), "test2: test1", "check dIMCTX");
+
+my $im = Imager->new(xsize => 1, ysize => 1);
+error_dIMCTXim($im);
+is(_get_error(), "test1", "check dIMCTXim");
+
+ok(context_refs(), "check refcount functions");
+
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+ unlink "testout/t84inlinectx.log";
+}
+
+sub _get_error {
+ my @errors = Imager::i_errors();
+ return join(": ", map $_->[0], @errors);
+}
infix
invocant
metadata
+multi-threaded
+mutex
paletted
postfix
preload
for my $dir (@subdirs) {
my @changes = `git log --abbrev --oneline $last_tag..HEAD $dir`;
+ my @more_changes = `git status --porcelain $dir`;
SKIP:
{
- @changes or skip "No changes for $dir", 1;
+ @changes || @more_changes
+ or skip "No changes for $dir", 1;
my $vfile = "$dir/$dir.pm";
my $current = eval { MM->parse_version($vfile) };
my $last_rel_content = get_file_from_git($vfile, $last_tag);
my $last = eval { MM->parse_version(\$last_rel_content) };
- isnt($current, $last, "$dir updated, $vfile version bump");
+ unless (isnt($current, $last, "$dir updated, $vfile version bump")) {
+ diag(@changes, @more_changes);
+ }
}
}
Imager::Internal::Hlines T_PTROBJ
+Imager::Context T_PTROBJ
+
#############################################################################
INPUT