--- /dev/null
+package Imager::Font::FT2;
+use strict;
+use Imager;
+use vars qw($VERSION @ISA);
+@ISA = qw(Imager::Font);
+
+BEGIN {
+ $VERSION = "0.77";
+
+ eval {
+ require XSLoader;
+ XSLoader::load('Imager::Font::FT2', $VERSION);
+ 1;
+ } or do {
+ require DynaLoader;
+ push @ISA, 'DynaLoader';
+ bootstrap Imager::Font::FT2 $VERSION;
+ };
+}
+
+*_first = \&Imager::Font::_first;
+
+sub new {
+ my $class = shift;
+ my %hsh=(color=>Imager::Color->new(255,0,0,0),
+ size=>15,
+ @_);
+
+ unless ($hsh{file}) {
+ $Imager::ERRSTR = "No font file specified";
+ return;
+ }
+ unless (-e $hsh{file}) {
+ $Imager::ERRSTR = "Font file $hsh{file} not found";
+ return;
+ }
+ unless ($Imager::formats{ft2}) {
+ $Imager::ERRSTR = "Freetype2 not supported in this build";
+ return;
+ }
+ my $id = i_ft2_new($hsh{file}, $hsh{'index'} || 0);
+ unless ($id) { # the low-level code may miss some error handling
+ $Imager::ERRSTR = Imager::_error_as_msg();
+ return;
+ }
+ return bless {
+ id => $id,
+ aa => $hsh{aa} || 0,
+ file => $hsh{file},
+ type => 't1',
+ size => $hsh{size},
+ color => $hsh{color},
+ utf8 => $hsh{utf8},
+ vlayout => $hsh{vlayout},
+ }, $class;
+}
+
+sub _draw {
+ my $self = shift;
+ my %input = @_;
+ if (exists $input{channel}) {
+ i_ft2_cp($self->{id}, $input{image}{IMG}, $input{'x'}, $input{'y'},
+ $input{channel}, $input{size}, $input{sizew} || 0,
+ $input{string}, , $input{align}, $input{aa}, $input{vlayout},
+ $input{utf8});
+ } else {
+ i_ft2_text($self->{id}, $input{image}{IMG},
+ $input{'x'}, $input{'y'},
+ $input{color}, $input{size}, $input{sizew} || 0,
+ $input{string}, $input{align}, $input{aa}, $input{vlayout},
+ $input{utf8});
+ }
+}
+
+sub _bounding_box {
+ my $self = shift;
+ my %input = @_;
+
+ return i_ft2_bbox($self->{id}, $input{size}, $input{sizew}, $input{string},
+ $input{utf8});
+}
+
+sub dpi {
+ my $self = shift;
+ my @old = i_ft2_getdpi($self->{id});
+ if (@_) {
+ my %hsh = @_;
+ my $result;
+ unless ($hsh{xdpi} && $hsh{ydpi}) {
+ if ($hsh{dpi}) {
+ $hsh{xdpi} = $hsh{ydpi} = $hsh{dpi};
+ }
+ else {
+ $Imager::ERRSTR = "dpi method requires xdpi and ydpi or just dpi";
+ return;
+ }
+ i_ft2_setdpi($self->{id}, $hsh{xdpi}, $hsh{ydpi}) or return;
+ }
+ }
+
+ return @old;
+}
+
+sub hinting {
+ my ($self, %opts) = @_;
+
+ i_ft2_sethinting($self->{id}, $opts{hinting} || 0);
+}
+
+sub _transform {
+ my $self = shift;
+
+ my %hsh = @_;
+ my $matrix = $hsh{matrix} or return undef;
+
+ return i_ft2_settransform($self->{id}, $matrix)
+}
+
+sub utf8 {
+ return 1;
+}
+
+# check if the font has the characters in the given string
+sub has_chars {
+ my ($self, %hsh) = @_;
+
+ unless (defined $hsh{string} && length $hsh{string}) {
+ $Imager::ERRSTR = "No string supplied to \$font->has_chars()";
+ return;
+ }
+ return i_ft2_has_chars($self->{id}, $hsh{string},
+ _first($hsh{'utf8'}, $self->{utf8}, 0));
+}
+
+sub face_name {
+ my ($self) = @_;
+
+ i_ft2_face_name($self->{id});
+}
+
+sub can_glyph_names {
+ i_ft2_can_do_glyph_names();
+}
+
+sub glyph_names {
+ my ($self, %input) = @_;
+
+ my $string = $input{string};
+ defined $string
+ or return Imager->_set_error("no string parameter passed to glyph_names");
+ my $utf8 = _first($input{utf8}, 0);
+ my $reliable_only = _first($input{reliable_only}, 1);
+
+ my @names = i_ft2_glyph_name($self->{id}, $string, $utf8, $reliable_only);
+ @names or return Imager->_set_error(Imager->_error_as_msg);
+
+ return @names if wantarray;
+ return pop @names;
+}
+
+sub is_mm {
+ my ($self) = @_;
+
+ i_ft2_is_multiple_master($self->{id});
+}
+
+sub mm_axes {
+ my ($self) = @_;
+
+ my ($num_axis, $num_design, @axes) =
+ i_ft2_get_multiple_masters($self->{id})
+ or return Imager->_set_error(Imager->_error_as_msg);
+
+ return @axes;
+}
+
+sub set_mm_coords {
+ my ($self, %opts) = @_;
+
+ $opts{coords}
+ or return Imager->_set_error("Missing coords parameter");
+ ref($opts{coords}) && $opts{coords} =~ /ARRAY\(0x[\da-f]+\)$/
+ or return Imager->_set_error("coords parameter must be an ARRAY ref");
+
+ i_ft2_set_mm_coords($self->{id}, @{$opts{coords}})
+ or return Imager->_set_error(Imager->_error_as_msg);
+
+ return 1;
+}
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::Font::FT2 - font support using FreeType 2
+
+=head1 SYNOPSIS
+
+ use Imager;
+
+ my $img = Imager->new;
+ my $font = Imager::Font->new(file => "foo.ttf", type => "ft2");
+
+ $img->string(... font => $font);
+
+=head1 DESCRIPTION
+
+This provides font support on FreeType 2.
+
+=head1 CAVEATS
+
+Unfortunately, older versions of Imager would install
+C<Imager::Font::FreeType2> even if FreeType 2 wasn't available, and if
+no font was created would succeed in loading the module. This means
+that an existing C<FreeType2.pm> could cause a probe success for
+supported font files, so I've renamed it.
+
+=head1 AUTHOR
+
+Tony Cook <tony@imager.perl.org>
+
+=head1 SEE ALSO
+
+Imager, Imager::Font.
+
+=cut
--- /dev/null
+#define PERL_NO_GET_CONTEXT
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "imext.h"
+#include "imperl.h"
+#include "imft2.h"
+
+DEFINE_IMAGER_CALLBACKS;
+
+MODULE = Imager::Font::FT2 PACKAGE = Imager::Font::FT2x PREFIX=FT2_
+
+#define FT2_DESTROY(font) i_ft2_destroy(font)
+
+void
+FT2_DESTROY(font)
+ Imager::Font::FT2x font
+
+int
+FT2_CLONE_SKIP(...)
+ CODE:
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
+MODULE = Imager::Font::FT2 PACKAGE = Imager::Font::FT2
+
+Imager::Font::FT2x
+i_ft2_new(name, index)
+ char *name
+ int index
+
+undef_int
+i_ft2_setdpi(font, xdpi, ydpi)
+ Imager::Font::FT2x font
+ int xdpi
+ int ydpi
+
+void
+i_ft2_getdpi(font)
+ Imager::Font::FT2x font
+ PREINIT:
+ int xdpi, ydpi;
+ CODE:
+ if (i_ft2_getdpi(font, &xdpi, &ydpi)) {
+ EXTEND(SP, 2);
+ PUSHs(sv_2mortal(newSViv(xdpi)));
+ PUSHs(sv_2mortal(newSViv(ydpi)));
+ }
+
+undef_int
+i_ft2_sethinting(font, hinting)
+ Imager::Font::FT2x font
+ int hinting
+
+undef_int
+i_ft2_settransform(font, matrix)
+ Imager::Font::FT2x font
+ PREINIT:
+ double matrix[6];
+ int len;
+ AV *av;
+ SV *sv1;
+ int i;
+ CODE:
+ if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
+ croak("i_ft2_settransform: parameter 2 must be an array ref\n");
+ av=(AV*)SvRV(ST(1));
+ len=av_len(av)+1;
+ if (len > 6)
+ len = 6;
+ for (i = 0; i < len; ++i) {
+ sv1=(*(av_fetch(av,i,0)));
+ matrix[i] = SvNV(sv1);
+ }
+ for (; i < 6; ++i)
+ matrix[i] = 0;
+ RETVAL = i_ft2_settransform(font, matrix);
+ OUTPUT:
+ RETVAL
+
+void
+i_ft2_bbox(font, cheight, cwidth, text_sv, utf8)
+ Imager::Font::FT2x font
+ double cheight
+ double cwidth
+ SV *text_sv
+ int utf8
+ PREINIT:
+ int bbox[BOUNDING_BOX_COUNT];
+ int i;
+ char *text;
+ STRLEN text_len;
+ int rc;
+ PPCODE:
+ text = SvPV(text_sv, text_len);
+#ifdef SvUTF8
+ if (SvUTF8(text_sv))
+ utf8 = 1;
+#endif
+ rc = i_ft2_bbox(font, cheight, cwidth, text, text_len, bbox, utf8);
+ if (rc) {
+ EXTEND(SP, rc);
+ for (i = 0; i < rc; ++i)
+ PUSHs(sv_2mortal(newSViv(bbox[i])));
+ }
+
+void
+i_ft2_bbox_r(font, cheight, cwidth, text, vlayout, utf8)
+ Imager::Font::FT2x font
+ double cheight
+ double cwidth
+ char *text
+ int vlayout
+ int utf8
+ PREINIT:
+ int bbox[8];
+ int i;
+ PPCODE:
+#ifdef SvUTF8
+ if (SvUTF8(ST(3)))
+ utf8 = 1;
+#endif
+ if (i_ft2_bbox_r(font, cheight, cwidth, text, strlen(text), vlayout,
+ utf8, bbox)) {
+ EXTEND(SP, 8);
+ for (i = 0; i < 8; ++i)
+ PUSHs(sv_2mortal(newSViv(bbox[i])));
+ }
+
+undef_int
+i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, align, aa, vlayout, utf8)
+ Imager::Font::FT2x font
+ Imager::ImgRaw im
+ int tx
+ int ty
+ Imager::Color cl
+ double cheight
+ double cwidth
+ int align
+ int aa
+ int vlayout
+ int utf8
+ PREINIT:
+ char *text;
+ STRLEN len;
+ CODE:
+#ifdef SvUTF8
+ if (SvUTF8(ST(7))) {
+ utf8 = 1;
+ }
+#endif
+ text = SvPV(ST(7), len);
+ RETVAL = i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text,
+ len, align, aa, vlayout, utf8);
+ OUTPUT:
+ RETVAL
+
+undef_int
+i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text_sv, align, aa, vlayout, utf8)
+ Imager::Font::FT2x font
+ Imager::ImgRaw im
+ int tx
+ int ty
+ int channel
+ double cheight
+ double cwidth
+ SV *text_sv
+ int align
+ int aa
+ int vlayout
+ int utf8
+ PREINIT:
+ char const *text;
+ STRLEN len;
+ CODE:
+#ifdef SvUTF8
+ if (SvUTF8(ST(7)))
+ utf8 = 1;
+#endif
+ text = SvPV(text_sv, len);
+ RETVAL = i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text,
+ len, align, aa, vlayout, 1);
+ OUTPUT:
+ RETVAL
+
+void
+ft2_transform_box(font, x0, x1, x2, x3)
+ Imager::Font::FT2x font
+ int x0
+ int x1
+ int x2
+ int x3
+ PREINIT:
+ int box[4];
+ PPCODE:
+ box[0] = x0; box[1] = x1; box[2] = x2; box[3] = x3;
+ ft2_transform_box(font, box);
+ EXTEND(SP, 4);
+ PUSHs(sv_2mortal(newSViv(box[0])));
+ PUSHs(sv_2mortal(newSViv(box[1])));
+ PUSHs(sv_2mortal(newSViv(box[2])));
+ PUSHs(sv_2mortal(newSViv(box[3])));
+
+void
+i_ft2_has_chars(handle, text_sv, utf8)
+ Imager::Font::FT2x handle
+ SV *text_sv
+ int utf8
+ PREINIT:
+ char *text;
+ STRLEN len;
+ char *work;
+ int count;
+ int i;
+ PPCODE:
+#ifdef SvUTF8
+ if (SvUTF8(text_sv))
+ utf8 = 1;
+#endif
+ text = SvPV(text_sv, len);
+ work = mymalloc(len);
+ count = i_ft2_has_chars(handle, text, len, utf8, work);
+ if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ PUSHs(sv_2mortal(newSViv(work[i])));
+ }
+ }
+ else {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv(work, count)));
+ }
+ myfree(work);
+
+void
+i_ft2_face_name(handle)
+ Imager::Font::FT2x handle
+ PREINIT:
+ char name[255];
+ int len;
+ PPCODE:
+ len = i_ft2_face_name(handle, name, sizeof(name));
+ if (len) {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+
+undef_int
+i_ft2_can_face_name()
+
+void
+i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
+ Imager::Font::FT2x handle
+ SV *text_sv
+ int utf8
+ int reliable_only
+ PREINIT:
+ char const *text;
+ STRLEN work_len;
+ size_t len;
+ char name[255];
+ PPCODE:
+#ifdef SvUTF8
+ if (SvUTF8(text_sv))
+ utf8 = 1;
+#endif
+ text = SvPV(text_sv, work_len);
+ len = work_len;
+ while (len) {
+ unsigned long ch;
+ if (utf8) {
+ ch = i_utf8_advance(&text, &len);
+ if (ch == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ break;
+ }
+ }
+ else {
+ ch = *text++;
+ --len;
+ }
+ EXTEND(SP, 1);
+ if (i_ft2_glyph_name(handle, ch, name, sizeof(name),
+ reliable_only)) {
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+ else {
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+int
+i_ft2_can_do_glyph_names()
+
+int
+i_ft2_face_has_glyph_names(handle)
+ Imager::Font::FT2x handle
+
+int
+i_ft2_is_multiple_master(handle)
+ Imager::Font::FT2x handle
+
+void
+i_ft2_get_multiple_masters(handle)
+ Imager::Font::FT2x handle
+ PREINIT:
+ i_font_mm mm;
+ int i;
+ PPCODE:
+ if (i_ft2_get_multiple_masters(handle, &mm)) {
+ EXTEND(SP, 2+mm.num_axis);
+ PUSHs(sv_2mortal(newSViv(mm.num_axis)));
+ PUSHs(sv_2mortal(newSViv(mm.num_designs)));
+ for (i = 0; i < mm.num_axis; ++i) {
+ AV *av = newAV();
+ SV *sv;
+ av_extend(av, 3);
+ sv = newSVpv(mm.axis[i].name, strlen(mm.axis[i].name));
+ SvREFCNT_inc(sv);
+ av_store(av, 0, sv);
+ sv = newSViv(mm.axis[i].minimum);
+ SvREFCNT_inc(sv);
+ av_store(av, 1, sv);
+ sv = newSViv(mm.axis[i].maximum);
+ SvREFCNT_inc(sv);
+ av_store(av, 2, sv);
+ PUSHs(newRV_noinc((SV *)av));
+ }
+ }
+
+undef_int
+i_ft2_set_mm_coords(handle, ...)
+ Imager::Font::FT2x handle
+ PROTOTYPE: DISABLE
+ PREINIT:
+ long *coords;
+ int ix_coords, i;
+ CODE:
+ /* T_ARRAY handling by xsubpp seems to be busted in 5.6.1, so
+ transfer the array manually */
+ ix_coords = items-1;
+ coords = mymalloc(sizeof(long) * ix_coords);
+ for (i = 0; i < ix_coords; ++i) {
+ coords[i] = (long)SvIV(ST(1+i));
+ }
+ RETVAL = i_ft2_set_mm_coords(handle, ix_coords, coords);
+ myfree(coords);
+ OUTPUT:
+ RETVAL
+
+
+BOOT:
+ PERL_INITIALIZE_IMAGER_CALLBACKS;
--- /dev/null
+#!perl -w
+use strict;
+use ExtUtils::MakeMaker qw(WriteMakefile WriteEmptyMakefile);
+use Getopt::Long;
+use Config;
+
+my $verbose = $ENV{IM_VERBOSE};
+my @libpaths;
+my @incpaths;
+
+GetOptions("incpath=s", \@incpaths,
+ "libpath=s" => \@libpaths,
+ "verbose|v" => \$verbose);
+
+our $BUILDING_IMAGER;
+
+$DB::single = 1;
+
+my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
+
+my %opts =
+ (
+ NAME => 'Imager::Font::FT2',
+ VERSION_FROM => 'FT2.pm',
+ OBJECT => 'FT2.o freetyp2.o',
+ clean => { FILES => 'testout' },
+ );
+
+my @inc;
+if ($BUILDING_IMAGER) {
+ push @inc, "-I..";
+ unshift @INC, "../lib";
+}
+else {
+ unshift @INC, "inc";
+ print "FreeType 2: building independently\n";
+ require Imager::ExtUtils;
+ push @inc, Imager::ExtUtils->includes;
+ $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ];
+
+ # Imager required configure through use
+ my @Imager_req = ( Imager => "0.77" );
+ if ($MM_ver >= 6.46) {
+ $opts{META_MERGE} =
+ {
+ configure_requires =>
+ {
+ @Imager_req,
+ },
+ build_requires =>
+ {
+ @Imager_req,
+ "Test::More" => "0.47",
+ },
+ resources =>
+ {
+ homepage => "http://imager.perl.org/",
+ repository =>
+ {
+ url => "http://imager.perl.org/svn/trunk/Imager-Font-FT2",
+ web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager-Font-FT2",
+ type => "svn",
+ },
+ },
+ };
+ $opts{PREREQ_PM} =
+ {
+ @Imager_req,
+ };
+ }
+}
+
+require Imager::Probe;
+
+my %probe =
+ (
+ name => "FreeType 2",
+ code => \&freetype2_probe_ftconfig,
+ inccheck =>
+ sub { -e File::Spec->catfile($_[0], "freetype/ftbitmap.h") },
+ libbase => "freetype",
+ testcode => _ft2_test_code(),
+ testcodeheaders => [ "stdio.h", "string.h", "ft2build.h" ],
+ incpath => join($Config{path_sep}, @incpaths),
+ libpath => join($Config{path_sep}, @libpaths),
+ alternatives =>
+ [
+ {
+ incsuffix => "freetype2",
+ },
+ {
+ incsuffix => "freetype",
+ },
+ ],
+ );
+
+my $probe_res = Imager::Probe->probe(\%probe);
+if ($probe_res) {
+ push @inc, $probe_res->{INC};
+ $opts{LIBS} = $probe_res->{LIBS};
+
+ $opts{INC} = "@inc";
+
+ if ($MM_ver > 6.06) {
+ $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+ $opts{ABSTRACT} = 'FreeType 2 font driver for Imager';
+ }
+
+ WriteMakefile(%opts);
+}
+else {
+ if ($BUILDING_IMAGER) {
+ WriteEmptyMakefile(%opts);
+ }
+ else {
+ # fail in good way
+ die "OS unsupported: FreeType 2 headers/libraries not found\n";
+ }
+}
+
+sub _ft2_test_code {
+ return <<'CODE';
+return 0;
+CODE
+}
+
+sub is_exe {
+ my ($name) = @_;
+
+ my @exe_suffix = $Config{_exe};
+ if ($^O eq 'MSWin32') {
+ push @exe_suffix, qw/.bat .cmd/;
+ }
+
+ for my $dir (File::Spec->path) {
+ for my $suffix (@exe_suffix) {
+ -x catfile($dir, "$name$suffix")
+ and return 1;
+ }
+ }
+
+ return;
+}
+
+# probes for freetype2 by trying to run freetype-config
+sub freetype2_probe_ftconfig {
+ my ($req) = @_;
+
+ is_exe('freetype-config') or return;
+
+ my $cflags = `freetype-config --cflags`
+ and !$? or return;
+ chomp $cflags;
+
+ my $lflags = `freetype-config --libs`
+ and !$? or return;
+ chomp $lflags;
+
+ # before 2.1.5 freetype-config --cflags could output
+ # the -I options in the wrong order, causing a conflict with
+ # freetype1.x installed with the same --prefix
+ #
+ # can happen iff:
+ # - both -Iprefix/include and -Iprefix/include/freetype2 are in cflags
+ # in that order
+ # - freetype 1.x headers are in prefix/include/freetype
+ my @incdirs = map substr($_, 2), grep /^-I/, split ' ', $cflags;
+ if (@incdirs == 2
+ && $incdirs[1] eq "$incdirs[0]/freetype2"
+ && -e "$incdirs[0]/freetype/freetype.h"
+ && -e "$incdirs[0]/freetype/fterrid.h") {
+ print "** freetype-config provided -I options out of order, correcting\n"
+ if $verbose;
+ $cflags = join(' ', grep(!/-I/, split ' ', $cflags),
+ map "-I$_", reverse @incdirs);
+ }
+
+ print "$req->{name}: configured via freetype-config\n";
+
+ return
+ {
+ INC => $cflags,
+ LIBS => $lflags,
+ };
+}
--- /dev/null
+StartFontMetrics 2.0
+Comment Generated by pfaedit
+Comment Creation Date: Thu Jul 20 21:57:23 2006
+FontName ExistenceTest
+FullName ExistenceTest
+FamilyName ExistenceTest
+Weight Medium
+Notice (Created by Tony Cook,,, with PfaEdit 1.0 (http://pfaedit.sf.net))
+ItalicAngle 0
+IsFixedPitch false
+UnderlinePosition -100
+UnderlineThickness 50
+Version 001.000
+EncodingScheme AdobeStandardEncoding
+FontBBox -60 -55 819 775
+StartCharMetrics 3
+C 0 ; WX 487 ; N uniFFFD ; B 72 87 414 396 ;
+C 33 ; WX 310 ; N exclam ; B 51 0 207 738 ;
+C 47 ; WX 761 ; N slash ; B -60 -55 819 775 ;
+EndCharMetrics
+EndFontMetrics
--- /dev/null
+/*
+=head1 NAME
+
+freetyp2.c - font support via the FreeType library version 2.
+
+=head1 SYNOPSIS
+
+ if (!i_ft2_init()) { error }
+ FT2_Fonthandle *font;
+ font = i_ft2_new(name, index);
+ if (!i_ft2_setdpi(font, xdpi, ydpi)) { error }
+ if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
+ double matrix[6];
+ if (!i_ft2_settransform(font, matrix)) { error }
+ int bbox[BOUNDING_BOX_COUNT];
+ if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
+ i_img *im = ...;
+ i_color cl;
+ if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
+ aa)) { error }
+ if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
+ align, aa)) { error }
+ i_ft2_destroy(font);
+
+=head1 DESCRIPTION
+
+Implements Imager font support using the FreeType2 library.
+
+The FreeType2 library understands several font file types, including
+Truetype, Type1 and Windows FNT.
+
+=over
+
+=cut
+*/
+
+#include "imext.h"
+#include "imft2.h"
+#include <stdio.h>
+#include <math.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#ifdef FT_MULTIPLE_MASTERS_H
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+#define IM_FT2_MM
+#include FT_MULTIPLE_MASTERS_H
+#endif
+#endif
+
+static void ft2_push_message(int code);
+
+static int ft2_initialized = 0;
+static FT_Library library;
+
+static int i_min(int a, int b);
+static int i_max(int a, int b);
+
+/*
+=item i_ft2_init(void)
+
+Initializes the Freetype 2 library.
+
+Returns true on success, false on failure.
+
+=cut
+*/
+int
+i_ft2_init(void) {
+ FT_Error error;
+
+ i_clear_error();
+ error = FT_Init_FreeType(&library);
+ if (error) {
+ ft2_push_message(error);
+ i_push_error(0, "Initializing Freetype2");
+ return 0;
+ }
+
+ ft2_initialized = 1;
+
+ return 1;
+}
+
+struct FT2_Fonthandle {
+ FT_Face face;
+ int xdpi, ydpi;
+ int hint;
+ FT_Encoding encoding;
+
+ /* used to adjust so we can align the draw point to the top-left */
+ double matrix[6];
+
+#ifdef IM_FT2_MM
+ /* Multiple master data if any */
+ int has_mm;
+ FT_Multi_Master mm;
+#endif
+};
+
+/* the following is used to select a "best" encoding */
+static struct enc_score {
+ FT_Encoding encoding;
+ int score;
+} enc_scores[] =
+{
+ /* the selections here are fairly arbitrary
+ ideally we need to give the user a list of encodings available
+ and a mechanism to choose one */
+ { ft_encoding_unicode, 10 },
+ { ft_encoding_sjis, 8 },
+ { ft_encoding_gb2312, 8 },
+ { ft_encoding_big5, 8 },
+ { ft_encoding_wansung, 8 },
+ { ft_encoding_johab, 8 },
+ { ft_encoding_latin_2, 6 },
+ { ft_encoding_apple_roman, 6 },
+ { ft_encoding_adobe_standard, 6 },
+ { ft_encoding_adobe_expert, 6 },
+};
+
+/*
+=item i_ft2_new(char *name, int index)
+
+Creates a new font object, from the file given by I<name>. I<index>
+is the index of the font in a file with multiple fonts, where 0 is the
+first font.
+
+Return NULL on failure.
+
+=cut
+*/
+
+FT2_Fonthandle *
+i_ft2_new(const char *name, int index) {
+ FT_Error error;
+ FT2_Fonthandle *result;
+ FT_Face face;
+ int i, j;
+ FT_Encoding encoding;
+ int score;
+
+ mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
+
+ if (!ft2_initialized && !i_ft2_init())
+ return NULL;
+
+ i_clear_error();
+ error = FT_New_Face(library, name, index, &face);
+ if (error) {
+ ft2_push_message(error);
+ i_push_error(error, "Opening face");
+ mm_log((2, "error opening face '%s': %d\n", name, error));
+ return NULL;
+ }
+
+ encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
+ score = 0;
+ for (i = 0; i < face->num_charmaps; ++i) {
+ FT_Encoding enc_entry = face->charmaps[i]->encoding;
+ mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
+ enc_entry, face->charmaps[i]->platform_id,
+ face->charmaps[i]->encoding_id));
+ for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
+ if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
+ encoding = enc_entry;
+ score = enc_scores[j].score;
+ break;
+ }
+ }
+ }
+ FT_Select_Charmap(face, encoding);
+ mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
+
+ result = mymalloc(sizeof(FT2_Fonthandle));
+ result->face = face;
+ result->xdpi = result->ydpi = 72;
+ result->encoding = encoding;
+
+ /* by default we disable hinting on a call to i_ft2_settransform()
+ if we don't do this, then the hinting can the untransformed text
+ to be a different size to the transformed text.
+ Obviously we have it initially enabled.
+ */
+ result->hint = 1;
+
+ /* I originally forgot this: :/ */
+ /*i_ft2_settransform(result, matrix); */
+ result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
+ result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
+
+#ifdef IM_FT2_MM
+ {
+ FT_Multi_Master *mm = &result->mm;
+ int i;
+
+ if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0
+ && (error = FT_Get_Multi_Master(face, mm)) == 0) {
+ mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
+ for (i = 0; i < mm->num_axis; ++i) {
+ mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
+ (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
+ }
+ result->has_mm = 1;
+ }
+ else {
+ mm_log((2, "No multiple masters\n"));
+ result->has_mm = 0;
+ }
+ }
+#endif
+
+ return result;
+}
+
+/*
+=item i_ft2_destroy(FT2_Fonthandle *handle)
+
+Destroys a font object, which must have been the return value of
+i_ft2_new().
+
+=cut
+*/
+void
+i_ft2_destroy(FT2_Fonthandle *handle) {
+ FT_Done_Face(handle->face);
+ myfree(handle);
+}
+
+/*
+=item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
+
+Sets the resolution in dots per inch at which point sizes scaled, by
+default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
+
+Both xdpi and ydpi should be positive.
+
+Return true on success.
+
+=cut
+*/
+int
+i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
+ i_clear_error();
+ if (xdpi > 0 && ydpi > 0) {
+ handle->xdpi = xdpi;
+ handle->ydpi = ydpi;
+ return 0;
+ }
+ else {
+ i_push_error(0, "resolutions must be positive");
+ return 0;
+ }
+}
+
+/*
+=item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
+
+Retrieves the current horizontal and vertical resolutions at which
+point sizes are scaled.
+
+=cut
+*/
+int
+i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
+ *xdpi = handle->xdpi;
+ *ydpi = handle->ydpi;
+
+ return 1;
+}
+
+/*
+=item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
+
+Sets a transormation matrix for output.
+
+This should be a 2 x 3 matrix like:
+
+ matrix[0] matrix[1] matrix[2]
+ matrix[3] matrix[4] matrix[5]
+
+=cut
+*/
+int
+i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix) {
+ FT_Matrix m;
+ FT_Vector v;
+ int i;
+
+ m.xx = matrix[0] * 65536;
+ m.xy = matrix[1] * 65536;
+ v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
+ m.yx = matrix[3] * 65536;
+ m.yy = matrix[4] * 65536;
+ v.y = matrix[5]; /* see just above */
+
+ FT_Set_Transform(handle->face, &m, &v);
+
+ for (i = 0; i < 6; ++i)
+ handle->matrix[i] = matrix[i];
+ handle->hint = 0;
+
+ return 1;
+}
+
+/*
+=item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
+
+If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
+
+i_ft2_settransform() disables hinting to prevent distortions in
+gradual text transformations.
+
+=cut
+*/
+int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
+ handle->hint = hinting;
+ return 1;
+}
+
+/*
+=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int *bbox)
+
+Retrieves bounding box information for the font at the given
+character width and height. This ignores the transformation matrix.
+
+Returns non-zero on success.
+
+=cut
+*/
+int
+i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
+ char const *text, size_t len, int *bbox, int utf8) {
+ FT_Error error;
+ int width;
+ int index;
+ int first;
+ int ascent = 0, descent = 0;
+ int glyph_ascent, glyph_descent;
+ FT_Glyph_Metrics *gm;
+ int start = 0;
+ int loadFlags = FT_LOAD_DEFAULT;
+ int rightb = 0;
+
+ mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
+ handle, cheight, cwidth, text, len, bbox));
+
+ error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
+ handle->xdpi, handle->ydpi);
+ if (error) {
+ ft2_push_message(error);
+ i_push_error(0, "setting size");
+ }
+
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
+
+ first = 1;
+ width = 0;
+ while (len) {
+ unsigned long c;
+ 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;
+ }
+
+ index = FT_Get_Char_Index(handle->face, c);
+ error = FT_Load_Glyph(handle->face, index, loadFlags);
+ if (error) {
+ ft2_push_message(error);
+ i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
+ c, index);
+ return 0;
+ }
+ gm = &handle->face->glyph->metrics;
+ glyph_ascent = gm->horiBearingY / 64;
+ glyph_descent = glyph_ascent - gm->height/64;
+ if (first) {
+ start = gm->horiBearingX / 64;
+ /* handles -ve values properly */
+ ascent = glyph_ascent;
+ descent = glyph_descent;
+ first = 0;
+ }
+
+ if (glyph_ascent > ascent)
+ ascent = glyph_ascent;
+ if (glyph_descent < descent)
+ descent = glyph_descent;
+
+ width += gm->horiAdvance / 64;
+
+ if (len == 0) {
+ /* last character
+ handle the case where the right the of the character overlaps the
+ right*/
+ rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
+ /*if (rightb > 0)
+ rightb = 0;*/
+ }
+ }
+
+ bbox[BBOX_NEG_WIDTH] = start;
+ bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
+ bbox[BBOX_POS_WIDTH] = width;
+ if (rightb < 0)
+ bbox[BBOX_POS_WIDTH] -= rightb;
+ bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
+ bbox[BBOX_DESCENT] = descent;
+ bbox[BBOX_ASCENT] = ascent;
+ bbox[BBOX_ADVANCE_WIDTH] = width;
+ bbox[BBOX_RIGHT_BEARING] = rightb;
+ mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
+
+ return BBOX_RIGHT_BEARING + 1;
+}
+
+/*
+=item transform_box(FT2_FontHandle *handle, int bbox[4])
+
+bbox contains coorinates of a the top-left and bottom-right of a bounding
+box relative to a point.
+
+This is then transformed and the values in bbox[4] are the top-left
+and bottom-right of the new bounding box.
+
+This is meant to provide the bounding box of a transformed character
+box. The problem is that if the character was round and is rotated,
+the real bounding box isn't going to be much different from the
+original, but this function will return a _bigger_ bounding box. I
+suppose I could work my way through the glyph outline, but that's
+too much hard work.
+
+=cut
+*/
+void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
+ double work[8];
+ double *matrix = handle->matrix;
+
+ work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
+ work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
+ work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
+ work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
+ work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
+ work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
+ work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
+ work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
+
+ bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
+ bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
+ bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
+ bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
+}
+
+/*
+=item expand_bounds(int bbox[4], int bbox2[4])
+
+Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
+bounding box in bbox[] that encloses both.
+
+=cut
+*/
+static void expand_bounds(int bbox[4], int bbox2[4]) {
+ bbox[0] = i_min(bbox[0], bbox2[0]);
+ bbox[1] = i_min(bbox[1], bbox2[1]);
+ bbox[2] = i_max(bbox[2], bbox2[2]);
+ bbox[3] = i_max(bbox[3], bbox2[3]);
+}
+
+/*
+=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, int *bbox)
+
+Retrieves bounding box information for the font at the given
+character width and height.
+
+This version finds the rectangular bounding box of the glyphs, with
+the text as transformed by the transformation matrix. As with
+i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
+the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
+this could be near the bottom left corner of the box.
+
+(bbox[4], bbox[5]) is the offset to the start of the baseline.
+(bbox[6], bbox[7]) is the offset from the start of the baseline to the
+end of the baseline.
+
+Returns non-zero on success.
+
+=cut
+*/
+int
+i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
+ char const *text, size_t len, int vlayout, int utf8, int *bbox) {
+ FT_Error error;
+ int width;
+ int index;
+ int first;
+ int ascent = 0, descent = 0;
+ int glyph_ascent, glyph_descent;
+ FT_Glyph_Metrics *gm;
+ int work[4];
+ int bounds[4];
+ double x = 0, y = 0;
+ int i;
+ FT_GlyphSlot slot;
+ int loadFlags = FT_LOAD_DEFAULT;
+
+ if (vlayout)
+ loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
+
+ error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
+ handle->xdpi, handle->ydpi);
+ if (error) {
+ ft2_push_message(error);
+ i_push_error(0, "setting size");
+ }
+
+ first = 1;
+ width = 0;
+ while (len) {
+ unsigned long c;
+ 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;
+ }
+
+ index = FT_Get_Char_Index(handle->face, c);
+ error = FT_Load_Glyph(handle->face, index, loadFlags);
+ if (error) {
+ ft2_push_message(error);
+ i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
+ c, index);
+ return 0;
+ }
+ slot = handle->face->glyph;
+ gm = &slot->metrics;
+
+ /* these probably don't mean much for vertical layouts */
+ glyph_ascent = gm->horiBearingY / 64;
+ glyph_descent = glyph_ascent - gm->height/64;
+ if (vlayout) {
+ work[0] = gm->vertBearingX;
+ work[1] = gm->vertBearingY;
+ }
+ else {
+ work[0] = gm->horiBearingX;
+ work[1] = gm->horiBearingY;
+ }
+ work[2] = gm->width + work[0];
+ work[3] = work[1] - gm->height;
+ if (first) {
+ bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
+ bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
+ bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
+ bbox[5] /= 64;
+ }
+ ft2_transform_box(handle, work);
+ for (i = 0; i < 4; ++i)
+ work[i] /= 64;
+ work[0] += x;
+ work[1] += y;
+ work[2] += x;
+ work[3] += y;
+ if (first) {
+ for (i = 0; i < 4; ++i)
+ bounds[i] = work[i];
+ ascent = glyph_ascent;
+ descent = glyph_descent;
+ first = 0;
+ }
+ else {
+ expand_bounds(bounds, work);
+ }
+ x += slot->advance.x / 64;
+ y += slot->advance.y / 64;
+
+ if (glyph_ascent > ascent)
+ ascent = glyph_ascent;
+ if (glyph_descent > descent)
+ descent = glyph_descent;
+
+ if (len == 0) {
+ /* last character
+ handle the case where the right the of the character overlaps the
+ right*/
+ /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
+ if (rightb < 0)
+ width -= rightb / 64;*/
+ }
+ }
+
+ /* at this point bounds contains the bounds relative to the CP,
+ and x, y hold the final position relative to the CP */
+ /*bounds[0] -= x;
+ bounds[1] -= y;
+ bounds[2] -= x;
+ bounds[3] -= y;*/
+
+ bbox[0] = bounds[0];
+ bbox[1] = -bounds[3];
+ bbox[2] = bounds[2];
+ bbox[3] = -bounds[1];
+ bbox[6] = x;
+ bbox[7] = -y;
+
+ return 1;
+}
+
+static int
+make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
+
+/*
+=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
+
+Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
+I<cheight> and I<cwidth>.
+
+If align is 0, then the text is rendered with the top-left of the
+first character at (I<tx>, I<ty>). If align is non-zero then the text
+is rendered with (I<tx>, I<ty>) aligned with the base-line of the
+characters.
+
+If aa is non-zero then the text is anti-aliased.
+
+Returns non-zero on success.
+
+=cut
+*/
+int
+i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
+ double cheight, double cwidth, char const *text, size_t len,
+ int align, int aa, int vlayout, int utf8) {
+ FT_Error error;
+ int index;
+ FT_Glyph_Metrics *gm;
+ int bbox[BOUNDING_BOX_COUNT];
+ FT_GlyphSlot slot;
+ int x, y;
+ unsigned char *bmp;
+ unsigned char map[256];
+ char last_mode = ft_pixel_mode_none;
+ int last_grays = -1;
+ int loadFlags = FT_LOAD_DEFAULT;
+ i_render *render;
+
+ mm_log((1, "i_ft2_text(handle %p, im %p, tx %d, ty %d, cl %p, cheight %f, cwidth %f, text %p, len %d, align %d, aa %d)\n",
+ handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
+
+ if (vlayout) {
+ if (!FT_HAS_VERTICAL(handle->face)) {
+ i_push_error(0, "face has no vertical metrics");
+ return 0;
+ }
+ loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+ }
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
+
+ /* set the base-line based on the string ascent */
+ if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
+ return 0;
+
+ if (aa)
+ render = i_render_new(im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
+
+ if (!align) {
+ /* this may need adjustment */
+ tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
+ ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
+ }
+ while (len) {
+ unsigned long c;
+ 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;
+ }
+
+ index = FT_Get_Char_Index(handle->face, c);
+ error = FT_Load_Glyph(handle->face, index, loadFlags);
+ if (error) {
+ ft2_push_message(error);
+ i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
+ c, index);
+ if (aa)
+ i_render_delete(render);
+ return 0;
+ }
+ slot = handle->face->glyph;
+ gm = &slot->metrics;
+
+ if (gm->width) {
+ error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
+ if (error) {
+ ft2_push_message(error);
+ i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
+ if (aa)
+ i_render_delete(render);
+ return 0;
+ }
+ if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
+ bmp = slot->bitmap.buffer;
+ for (y = 0; y < slot->bitmap.rows; ++y) {
+ int pos = 0;
+ int bit = 0x80;
+ for (x = 0; x < slot->bitmap.width; ++x) {
+ if (bmp[pos] & bit)
+ i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
+
+ bit >>= 1;
+ if (bit == 0) {
+ bit = 0x80;
+ ++pos;
+ }
+ }
+ bmp += slot->bitmap.pitch;
+ }
+ }
+ else {
+ /* grey scale or something we can treat as greyscale */
+ /* we create a map to convert from the bitmap values to 0-255 */
+ if (last_mode != slot->bitmap.pixel_mode
+ || last_grays != slot->bitmap.num_grays) {
+ if (!make_bmp_map(&slot->bitmap, map))
+ return 0;
+ last_mode = slot->bitmap.pixel_mode;
+ last_grays = slot->bitmap.num_grays;
+ }
+
+ bmp = slot->bitmap.buffer;
+ for (y = 0; y < slot->bitmap.rows; ++y) {
+ if (last_mode == ft_pixel_mode_grays &&
+ last_grays != 255) {
+ for (x = 0; x < slot->bitmap.width; ++x)
+ bmp[x] = map[bmp[x]];
+ }
+ i_render_color(render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
+ slot->bitmap.width, bmp, cl);
+ bmp += slot->bitmap.pitch;
+ }
+ }
+ }
+
+ tx += slot->advance.x / 64;
+ ty -= slot->advance.y / 64;
+ }
+
+ if (aa)
+ i_render_delete(render);
+
+ return 1;
+}
+
+/*
+=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa)
+
+Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
+I<cheight> and I<cwidth>.
+
+If align is 0, then the text is rendered with the top-left of the
+first character at (I<tx>, I<ty>). If align is non-zero then the text
+is rendered with (I<tx>, I<ty>) aligned with the base-line of the
+characters.
+
+If aa is non-zero then the text is anti-aliased.
+
+Returns non-zero on success.
+
+=cut
+*/
+
+int
+i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
+ double cheight, double cwidth, char const *text, size_t len, int align,
+ int aa, int vlayout, int utf8) {
+ int bbox[8];
+ i_img *work;
+ i_color cl, cl2;
+ int x, y;
+
+ mm_log((1, "i_ft2_cp(handle %p, im %p, tx %d, ty %d, channel %d, cheight %f, cwidth %f, text %p, len %d, ...)\n",
+ handle, im, tx, ty, channel, cheight, cwidth, text, len));
+
+ if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
+ i_push_error(0, "face has no vertical metrics");
+ return 0;
+ }
+
+ if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
+ return 0;
+
+ work = i_img_8_new(bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
+ cl.channel[0] = 255;
+ if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
+ text, len, 1, aa, vlayout, utf8))
+ return 0;
+
+ if (!align) {
+ tx -= bbox[4];
+ ty += bbox[5];
+ }
+
+ /* render to the specified channel */
+ /* this will be sped up ... */
+ for (y = 0; y < work->ysize; ++y) {
+ for (x = 0; x < work->xsize; ++x) {
+ i_gpix(work, x, y, &cl);
+ i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
+ cl2.channel[channel] = cl.channel[0];
+ i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
+ }
+ }
+ i_img_destroy(work);
+ return 1;
+}
+
+/*
+=item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
+
+Check if the given characters are defined by the font.
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
+ int utf8, char *out) {
+ int count = 0;
+ mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
+ handle, text, 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;
+ }
+
+ index = FT_Get_Char_Index(handle->face, c);
+ *out++ = index != 0;
+ ++count;
+ }
+
+ return count;
+}
+
+/* uses a method described in fterrors.h to build an error translation
+ function
+*/
+#undef __FTERRORS_H__
+#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
+#define FT_ERROR_START_LIST
+#define FT_ERROR_END_LIST
+
+/*
+=back
+
+=head2 Internal Functions
+
+These functions are used in the implementation of freetyp2.c and should not
+(usually cannot) be called from outside it.
+
+=over
+
+=item ft2_push_message(int code)
+
+Pushes an error message corresponding to code onto the error stack.
+
+=cut
+*/
+static void ft2_push_message(int code) {
+ char unknown[40];
+
+ switch (code) {
+#include FT_ERRORS_H
+ }
+
+ sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
+ i_push_error(code, unknown);
+}
+
+/*
+=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
+
+Creates a map to convert grey levels from the glyphs bitmap into
+values scaled 0..255.
+
+=cut
+*/
+static int
+make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
+ int scale;
+ int i;
+
+ switch (bitmap->pixel_mode) {
+ case ft_pixel_mode_grays:
+ scale = bitmap->num_grays;
+ break;
+
+ default:
+ i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
+ return 0;
+ }
+
+ /* build the table */
+ for (i = 0; i < scale; ++i)
+ map[i] = i * 255 / (bitmap->num_grays - 1);
+
+ return 1;
+}
+
+/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
+ positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
+#ifndef FREETYPE_PATCH
+#define FREETYPE_PATCH 4
+#endif
+
+/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
+#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
+/* #define IM_HAS_FACE_NAME 0 */
+
+/*
+=item i_ft2_face_name(handle, name_buf, name_buf_size)
+
+Fills the given buffer with the Postscript Face name of the font,
+if there is one.
+
+=cut
+*/
+
+int
+i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+#if IM_HAS_FACE_NAME
+ char const *name = FT_Get_Postscript_Name(handle->face);
+
+ i_clear_error();
+
+ if (name) {
+ 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 available");
+ *name_buf = '\0';
+
+ return 0;
+ }
+#else
+ i_clear_error();
+ i_push_error(0, "Freetype 2.0.6 or later required");
+ *name_buf = '\0';
+
+ return 0;
+#endif
+}
+
+int
+i_ft2_can_face_name(void) {
+ return IM_HAS_FACE_NAME;
+}
+
+/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
+/* well, I assume FREETYPE_MAJOR is 2, since we're here */
+#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
+#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
+#endif
+
+int
+i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
+ size_t name_buf_size, int reliable_only) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ i_clear_error();
+ *name_buf = '\0';
+ i_push_error(0, "FT2 configured without glyph name support");
+
+ return 0;
+#else
+ FT_UInt index;
+
+ i_clear_error();
+
+ if (!FT_HAS_GLYPH_NAMES(handle->face)) {
+ i_push_error(0, "no glyph names in font");
+ *name_buf = '\0';
+ return 0;
+ }
+ if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
+ i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
+ *name_buf = '\0';
+ return 0;
+ }
+
+ index = FT_Get_Char_Index(handle->face, ch);
+
+ if (index) {
+ FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
+ name_buf_size);
+ if (error) {
+ ft2_push_message(error);
+ *name_buf = '\0';
+ return 0;
+ }
+ if (*name_buf) {
+ return strlen(name_buf) + 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ i_push_error(0, "no glyph for that character");
+ *name_buf = 0;
+ return 0;
+ }
+#endif
+}
+
+int
+i_ft2_can_do_glyph_names(void) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+int
+i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ return 0;
+#else
+ return FT_Has_PS_Glyph_Names(handle->face);
+#endif
+}
+
+int
+i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
+ i_clear_error();
+#ifdef IM_FT2_MM
+ return handle->has_mm;
+#else
+ return 0;
+#endif
+}
+
+int
+i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
+#ifdef IM_FT2_MM
+ int i;
+ FT_Multi_Master *mms = &handle->mm;
+
+ i_clear_error();
+ if (!handle->has_mm) {
+ i_push_error(0, "Font has no multiple masters");
+ return 0;
+ }
+ mm->num_axis = mms->num_axis;
+ mm->num_designs = mms->num_designs;
+ for (i = 0; i < mms->num_axis; ++i) {
+ mm->axis[i].name = mms->axis[i].name;
+ mm->axis[i].minimum = mms->axis[i].minimum;
+ mm->axis[i].maximum = mms->axis[i].maximum;
+ }
+
+ return 1;
+#else
+ i_clear_error();
+ i_push_error(0, "Multiple master functions unavailable");
+ return 0;
+#endif
+}
+
+int
+i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
+#ifdef IM_FT2_MM
+ int i;
+ FT_Long ftcoords[T1_MAX_MM_AXIS];
+ FT_Error error;
+
+ i_clear_error();
+ if (!handle->has_mm) {
+ i_push_error(0, "Font has no multiple masters");
+ return 0;
+ }
+ if (coord_count != handle->mm.num_axis) {
+ i_push_error(0, "Number of MM coords doesn't match MM axis count");
+ return 0;
+ }
+ for (i = 0; i < coord_count; ++i)
+ ftcoords[i] = coords[i];
+
+ error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
+ if (error) {
+ ft2_push_message(error);
+ return 0;
+ }
+
+ return 1;
+#else
+ i_clear_error();
+ i_push_error(0, "Multiple master functions unavailable");
+
+ return 0;
+#endif
+}
+
+static int
+i_min(int a, int b) {
+ return a < b ? a : b;
+}
+
+static int
+i_max(int a, int b) {
+ return a > b ? a : b;
+}
+
+/*
+=back
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>, with a fair amount of help from
+reading the code in font.c.
+
+=head1 SEE ALSO
+
+font.c, Imager::Font(3), Imager(3)
+
+http://www.freetype.org/
+
+=cut
+*/
+
--- /dev/null
+#ifndef IMAGER_FT2_H
+#define IMAGER_FT2_H
+
+#include "imdatatypes.h"
+
+typedef struct FT2_Fonthandle FT2_Fonthandle;
+
+typedef FT2_Fonthandle* Imager__Font__FT2x;
+
+extern int i_ft2_init(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);
+extern int i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi);
+extern int i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix);
+extern int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting);
+extern int i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
+ char const *text, size_t len, int *bbox, int utf8);
+extern int i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
+ char const *text, size_t len, int vlayout, int utf8, int *bbox);
+extern int i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
+ const i_color *cl, double cheight, double cwidth,
+ char const *text, size_t len, int align, int aa,
+ int vlayout, int utf8);
+extern int i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
+ int channel, double cheight, double cwidth,
+ char const *text, size_t len, int align, int aa,
+ int vlayout, int utf8);
+extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
+ int utf8, char *work);
+extern int i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf,
+ size_t name_buf_size);
+extern int i_ft2_can_face_name(void);
+extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch,
+ char *name_buf, size_t name_buf_size,
+ int reliable_only);
+extern int i_ft2_can_do_glyph_names(void);
+extern int i_ft2_face_has_glyph_names(FT2_Fonthandle *handle);
+
+extern int i_ft2_get_multiple_masters(FT2_Fonthandle *handle,
+ i_font_mm *mm);
+extern int
+i_ft2_is_multiple_master(FT2_Fonthandle *handle);
+extern int
+i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords);
+#endif
+
--- /dev/null
+#!perl -w
+use strict;
+use Test::More tests => 189;
+use Cwd qw(getcwd abs_path);
+
+use Imager qw(:all);
+
+use Imager::Test qw(diff_text_with_nul is_color3);
+
+-d "testout" or mkdir "testout";
+
+init_log("testout/t38ft2font.log",2);
+
+my $deffont = "fontfiles/dodge.ttf";
+
+my @base_color = (64, 255, 64);
+
+SKIP:
+{
+ ok($Imager::formats{ft2}, "ft2 should be in %formats");
+
+ my $fontname=$ENV{'TTFONTTEST'} || $deffont;
+
+ -f $fontname or skip("cannot find fontfile $fontname", 188);
+
+
+ my $bgcolor=i_color_new(255,0,0,0);
+ my $overlay=Imager::ImgRaw::new(200,70,3);
+
+ my $ttraw=Imager::Font::FT2::i_ft2_new($fontname, 0);
+
+ $ttraw or print Imager::_error_as_msg(),"\n";
+ ok($ttraw, "loaded raw font");
+
+ my @bbox=Imager::Font::FT2::i_ft2_bbox($ttraw, 50.0, 0, 'XMCLH', 0);
+ print "#bbox @bbox\n";
+
+ is(@bbox, 8, "i_ft2_bbox() returns 8 values");
+
+ ok(Imager::Font::FT2::i_ft2_cp($ttraw,$overlay,5,50,1,50.0,50, 'XMCLH',1,1, 0, 0), "drawn to channel");
+ i_line($overlay,0,50,100,50,$bgcolor,1);
+
+ open(FH,">testout/t38ft2font.ppm") || die "cannot open testout/t38ft2font.ppm\n";
+ binmode(FH);
+ my $IO = Imager::io_new_fd(fileno(FH));
+ ok(i_writeppm_wiol($overlay, $IO), "saved image");
+ close(FH);
+
+ $bgcolor=i_color_set($bgcolor,200,200,200,0);
+ my $backgr=Imager::ImgRaw::new(500,300,3);
+
+ # i_tt_set_aa(2);
+ ok(Imager::Font::FT2::i_ft2_text($ttraw,$backgr,100,150,NC(255, 64, 64),200.0,50, 'MAW',1,1,0, 0), "drew MAW");
+ Imager::Font::FT2::i_ft2_settransform($ttraw, [0.9659, 0.2588, 0, -0.2588, 0.9659, 0 ]);
+ ok(Imager::Font::FT2::i_ft2_text($ttraw,$backgr,100,150,NC(0, 128, 0),200.0,50, 'MAW',0,1, 0, 0), "drew rotated MAW");
+ i_line($backgr, 0,150, 499, 150, NC(0, 0, 255),1);
+
+ open(FH,">testout/t38ft2font2.ppm") || die "cannot open testout/t38ft2font.ppm\n";
+ binmode(FH);
+ $IO = Imager::io_new_fd(fileno(FH));
+ ok(i_writeppm_wiol($backgr,$IO), "saved second image");
+ close(FH);
+
+ my $oof = Imager::Font->new(file=>$fontname, type=>'ft2', 'index'=>0);
+
+ ok($oof, "loaded OO font");
+
+ my $im = Imager->new(xsize=>400, ysize=>250);
+
+ ok($im->string(font=>$oof,
+ text=>"Via OO",
+ 'x'=>20,
+ 'y'=>20,
+ size=>60,
+ color=>NC(255, 128, 255),
+ aa => 1,
+ align=>0), "drawn through OO interface");
+ ok($oof->transform(matrix=>[1, 0.1, 0, 0, 1, 0]),
+ "set matrix via OO interface");
+ ok($im->string(font=>$oof,
+ text=>"Shear",
+ 'x'=>20,
+ 'y'=>40,
+ size=>60,
+ sizew=>50,
+ channel=>1,
+ aa=>1,
+ align=>1), "drawn transformed through OO");
+ use Imager::Matrix2d ':handy';
+ ok($oof->transform(matrix=>m2d_rotate(degrees=>-30)),
+ "set transform from m2d module");
+ ok($im->string(font=>$oof,
+ text=>"SPIN",
+ 'x'=>20,
+ 'y'=>50,
+ size=>50,
+ sizew=>40,
+ color=>NC(255,255,0),
+ aa => 1,
+ align=>0, vlayout=>0), "drawn first rotated");
+
+ ok($im->string(font=>$oof,
+ text=>"SPIN",
+ 'x'=>20,
+ 'y'=>50,
+ size=>50,
+ sizew=>40,
+ channel=>2,
+ aa => 1,
+ align=>0, vlayout=>0), "drawn second rotated");
+
+ $oof->transform(matrix=>m2d_identity());
+ $oof->hinting(hinting=>1);
+
+ # UTF8 testing
+ # the test font (dodge.ttf) only supports one character above 0xFF that
+ # I can see, 0x2010 HYPHEN (which renders the same as 0x002D HYPHEN MINUS)
+ # an attempt at utf8 support
+ # first attempt to use native perl UTF8
+ SKIP:
+ {
+ skip("no native UTF8 support in this version of perl", 1)
+ unless $] >= 5.006;
+ my $text;
+ # we need to do this in eval to prevent compile time errors in older
+ # versions
+ eval q{$text = "A\x{2010}A"}; # A, HYPHEN, A in our test font
+ #$text = "A".chr(0x2010)."A"; # this one works too
+ unless (ok($im->string(font=>$oof,
+ text=>$text,
+ 'x'=>20,
+ 'y'=>200,
+ size=>50,
+ color=>NC(0,255,0),
+ aa=>1), "drawn UTF natively")) {
+ print "# ",$im->errstr,"\n";
+ }
+ }
+
+ # an attempt using emulation of UTF8
+ my $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
+ #my $text = "A\xE2\x80\x90\x41\x{2010}";
+ #substr($text, -1, 0) = '';
+ unless (ok($im->string(font=>$oof,
+ text=>$text,
+ 'x'=>20,
+ 'y'=>230,
+ size=>50,
+ color=>NC(255,128,0),
+ aa=>1,
+ utf8=>1), "drawn UTF emulated")) {
+ print "# ",$im->errstr,"\n";
+ }
+
+ # just a bit of fun
+ # well it was - it demostrates what happens when you combine
+ # transformations and font hinting
+ for my $steps (0..39) {
+ $oof->transform(matrix=>m2d_rotate(degrees=>-$steps+5));
+ # demonstrates why we disable hinting on a doing a transform
+ # if the following line is enabled then the 0 degrees output sticks
+ # out a bit
+ # $oof->hinting(hinting=>1);
+ $im->string(font=>$oof,
+ text=>"SPIN",
+ 'x'=>160,
+ 'y'=>70,
+ size=>65,
+ color=>NC(255, $steps * 5, 200-$steps * 5),
+ aa => 1,
+ align=>0, );
+ }
+
+ $im->write(file=>'testout/t38_oo.ppm')
+ or print "# could not save OO output: ",$im->errstr,"\n";
+
+ my (@got) = $oof->has_chars(string=>"\x01H");
+ ok(@got == 2, "has_chars returned 2 items");
+ ok(!$got[0], "have no chr(1)");
+ ok($got[1], "have 'H'");
+ is($oof->has_chars(string=>"H\x01"), "\x01\x00",
+ "scalar has_chars()");
+
+ print "# OO bounding boxes\n";
+ @bbox = $oof->bounding_box(string=>"hello", size=>30);
+ my $bbox = $oof->bounding_box(string=>"hello", size=>30);
+
+ is(@bbox, 8, "list bbox returned 8 items");
+ ok($bbox->isa('Imager::Font::BBox'), "scalar bbox returned right class");
+ ok($bbox->start_offset == $bbox[0], "start_offset");
+ ok($bbox->end_offset == $bbox[2], "end_offset");
+ ok($bbox->global_ascent == $bbox[3], "global_ascent");
+ ok($bbox->global_descent == $bbox[1], "global_descent");
+ ok($bbox->ascent == $bbox[5], "ascent");
+ ok($bbox->descent == $bbox[4], "descent");
+ ok($bbox->advance_width == $bbox[6], "advance_width");
+
+ print "# aligned text output\n";
+ my $alimg = Imager->new(xsize=>300, ysize=>300);
+ $alimg->box(color=>'40FF40', filled=>1);
+
+ $oof->transform(matrix=>m2d_identity());
+ $oof->hinting(hinting=>1);
+
+ align_test('left', 'top', 10, 10, $oof, $alimg);
+ align_test('start', 'top', 10, 40, $oof, $alimg);
+ align_test('center', 'top', 150, 70, $oof, $alimg);
+ align_test('end', 'top', 290, 100, $oof, $alimg);
+ align_test('right', 'top', 290, 130, $oof, $alimg);
+
+ align_test('center', 'top', 150, 160, $oof, $alimg);
+ align_test('center', 'center', 150, 190, $oof, $alimg);
+ align_test('center', 'bottom', 150, 220, $oof, $alimg);
+ align_test('center', 'baseline', 150, 250, $oof, $alimg);
+
+ ok($alimg->write(file=>'testout/t38aligned.ppm'),
+ "saving aligned output image");
+
+ my $exfont = Imager::Font->new(file=>'fontfiles/ExistenceTest.ttf',
+ type=>'ft2');
+ SKIP:
+ {
+ ok($exfont, "loaded existence font")
+ or diag(Imager->errstr);
+ $exfont
+ or skip("couldn't load test font", 11);
+
+ # the test font is known to have a shorter advance width for that char
+ my @bbox = $exfont->bounding_box(string=>"/", size=>100);
+ is(@bbox, 8, "should be 8 entries");
+ isnt($bbox[6], $bbox[2], "different advance width");
+ my $bbox = $exfont->bounding_box(string=>"/", size=>100);
+ ok($bbox->pos_width != $bbox->advance_width, "OO check");
+
+ cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
+
+ cmp_ok($bbox->display_width, '>', $bbox->advance_width,
+ "check display width (roughly)");
+
+ # check with a char that fits inside the box
+ $bbox = $exfont->bounding_box(string=>"!", size=>100);
+ print "# pos width ", $bbox->pos_width, "\n";
+ is($bbox->pos_width, $bbox->advance_width,
+ "check backwards compatibility");
+ cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
+ cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
+ cmp_ok($bbox->display_width, '<', $bbox->advance_width,
+ "display smaller than advance");
+
+ # name tests
+ # make sure the number of tests on each branch match
+ if (Imager::Font::FT2::i_ft2_can_face_name()) {
+ my $facename = Imager::Font::FT2::i_ft2_face_name($exfont->{id});
+ print "# face name '$facename'\n";
+ is($facename, 'ExistenceTest', "test face name");
+ $facename = $exfont->face_name;
+ is($facename, 'ExistenceTest', "test face name OO");
+ }
+ else {
+ # make sure we get the error we expect
+ my $facename = Imager::Font::FT2::i_ft2_face_name($exfont->{id});
+ my ($msg) = Imager::_error_as_msg();
+ ok(!defined($facename), "test face name not supported");
+ print "# $msg\n";
+ ok(scalar($msg =~ /or later required/), "test face name not supported");
+ }
+ }
+
+ SKIP:
+ {
+ Imager::Font::FT2->can_glyph_names
+ or skip("FT2 compiled without glyph names support", 9);
+
+ # FT2 considers POST tables in TTF fonts unreliable, so use
+ # a type 1 font, see below for TTF test
+ my $exfont = Imager::Font->new(file=>'fontfiles/ExistenceTest.pfb',
+ type=>'ft2');
+ SKIP:
+ {
+ ok($exfont, "load Type 1 via FT2")
+ or skip("couldn't load type 1 with FT2", 8);
+ my @glyph_names =
+ Imager::Font::FT2::i_ft2_glyph_name($exfont->{id}, "!J/");
+ #use Data::Dumper;
+ #print Dumper \@glyph_names;
+ is($glyph_names[0], 'exclam', "check exclam name");
+ ok(!defined($glyph_names[1]), "check for no J name");
+ is($glyph_names[2], 'slash', "check slash name");
+
+ # oo interfaces
+ @glyph_names = $exfont->glyph_names(string=>"!J/");
+ is($glyph_names[0], 'exclam', "check exclam name OO");
+ ok(!defined($glyph_names[1]), "check for no J name OO");
+ is($glyph_names[2], 'slash', "check slash name OO");
+
+ # make sure a missing string parameter is handled correctly
+ eval {
+ $exfont->glyph_names();
+ };
+ is($@, "", "correct error handling");
+ cmp_ok(Imager->errstr, '=~', qr/no string parameter/, "error message");
+ }
+
+ # freetype 2 considers truetype glyph name tables unreliable
+ # due to some specific fonts, supplying reliable_only=>0 bypasses
+ # that check and lets us get the glyph names even for truetype fonts
+ # so we can test this stuff <sigh>
+ # we can't use ExistenceTest.ttf since that's generated with
+ # AppleStandardEncoding since the same .sfd needs to generate
+ # a .pfb file, NameTest.ttf uses a Unicode encoding
+
+ # we were using an unsigned char to store a unicode character
+ # https://rt.cpan.org/Ticket/Display.html?id=7949
+ $exfont = Imager::Font->new(file=>'fontfiles/NameTest.ttf',
+ type=>'ft2');
+ SKIP:
+ {
+ ok($exfont, "load TTF via FT2")
+ or skip("could not load TTF with FT2", 1);
+ my $text = pack("C*", 0xE2, 0x80, 0x90); # "\x{2010}" as utf-8
+ my @names = $exfont->glyph_names(string=>$text,
+ utf8=>1, reliable_only=>0);
+ is($names[0], "hyphentwo", "check utf8 glyph name");
+ }
+ }
+
+ # check that error codes are translated correctly
+ my $errfont = Imager::Font->new(file=>"t/t10ft2.t", type=>"ft2");
+ is($errfont, undef, "new font vs non font");
+ cmp_ok(Imager->errstr, '=~', qr/unknown file format/, "check error message");
+
+ # Multiple Master tests
+ # we check a non-MM font errors correctly
+ print "# check that the methods act correctly for a non-MM font\n";
+ ok(!$exfont->is_mm, "exfont not MM");
+ ok((() = $exfont->mm_axes) == 0, "exfont has no MM axes");
+ cmp_ok(Imager->errstr, '=~', qr/no multiple masters/,
+ "and returns correct error when we ask");
+ ok(!$exfont->set_mm_coords(coords=>[0, 0]), "fail setting axis on exfont");
+ cmp_ok(Imager->errstr, '=~', qr/no multiple masters/,
+ "and returns correct error when we ask");
+
+ # try a MM font now - test font only has A defined
+ print "# Try a multiple master font\n";
+ my $mmfont = Imager::Font->new(file=>"fontfiles/MMOne.pfb", type=>"ft2",
+ color=>"white", aa=>1, size=>60);
+ ok($mmfont, "loaded MM font");
+ ok($mmfont->is_mm, "font is multiple master");
+ my @axes = $mmfont->mm_axes;
+ is(@axes, 2, "check we got both axes");
+ is($axes[0][0], "Weight", "name of first axis");
+ is($axes[0][1], 50, "min for first axis");
+ is($axes[0][2], 999, "max for first axis");
+ is($axes[1][0], "Slant", "name of second axis");
+ is($axes[1][1], 0, "min for second axis");
+ is($axes[1][2], 999, "max for second axis");
+ my $mmim = Imager->new(xsize=>200, ysize=>200);
+ $mmim->string(font=>$mmfont, x=>0, 'y'=>50, text=>"A");
+ ok($mmfont->set_mm_coords(coords=>[ 700, 0 ]), "set to bold, unsloped");
+ $mmim->string(font=>$mmfont, x=>0, 'y'=>100, text=>"A", color=>'blue');
+ my @weights = qw(50 260 525 760 999);
+ my @slants = qw(0 333 666 999);
+ for my $windex (0 .. $#weights) {
+ my $weight = $weights[$windex];
+ for my $sindex (0 .. $#slants) {
+ my $slant = $slants[$sindex];
+ $mmfont->set_mm_coords(coords=>[ $weight, $slant ]);
+ $mmim->string(font=>$mmfont, x=>30+32*$windex, 'y'=>50+45*$sindex,
+ text=>"A");
+ }
+ }
+
+ ok($mmim->write(file=>"testout/t38mm.ppm"), "save MM output");
+
+ SKIP:
+ { print "# alignment tests\n";
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ ok($font, "loaded deffont OO")
+ or skip("could not load font:".Imager->errstr, 4);
+ my $im = Imager->new(xsize=>140, ysize=>150);
+ my %common =
+ (
+ font=>$font,
+ size=>40,
+ aa=>1,
+ );
+ $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
+ $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
+ $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
+ for my $args ([ x=>5, text=>"A", color=>"white" ],
+ [ x=>40, text=>"y", color=>"white" ],
+ [ x=>75, text=>"A", channel=>1 ],
+ [ x=>110, text=>"y", channel=>1 ]) {
+ ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
+ ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
+ ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
+ }
+ ok($im->write(file=>'testout/t38align.ppm'), "save align image");
+ }
+
+
+ { # outputting a space in non-AA could either crash
+ # or fail (ft 2.2+)
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ my $im = Imager->new(xsize => 100, ysize => 100);
+ ok($im->string(x => 10, y => 10, string => "test space", aa => 0,
+ color => '#FFF', size => 8, font => $font),
+ "draw space non-antialiased (color)");
+ ok($im->string(x => 10, y => 50, string => "test space", aa => 0,
+ channel => 0, size => 8, font => $font),
+ "draw space non-antialiased (channel)");
+ }
+
+ { # cannot output "0"
+ # https://rt.cpan.org/Ticket/Display.html?id=21770
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ ok($font, "loaded imugly");
+ my $imbase = Imager->new(xsize => 100, ysize => 100);
+ my $im = $imbase->copy;
+ ok($im->string(x => 10, y => 50, string => "0", aa => 0,
+ color => '#FFF', size => 20, font => $font),
+ "draw '0'");
+ ok(Imager::i_img_diff($im->{IMG}, $imbase->{IMG}),
+ "make sure we actually drew it");
+ $im = $imbase->copy;
+ ok($im->string(x => 10, y => 50, string => 0.0, aa => 0,
+ color => '#FFF', size => 20, font => $font),
+ "draw 0.0");
+ ok(Imager::i_img_diff($im->{IMG}, $imbase->{IMG}),
+ "make sure we actually drew it");
+ }
+ { # string output cut off at NUL ('\0')
+ # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ ok($font, "loaded imugly");
+
+ diff_text_with_nul("a\\0b vs a", "a\0b", "a",
+ font => $font, color => '#FFFFFF');
+ diff_text_with_nul("a\\0b vs a", "a\0b", "a",
+ font => $font, channel => 1);
+
+ # UTF8 encoded \x{2010}
+ my $dash = pack("C*", 0xE2, 0x80, 0x90);
+ diff_text_with_nul("utf8 dash\0dash vs dash", "$dash\0$dash", $dash,
+ font => $font, color => '#FFFFFF', utf8 => 1);
+ diff_text_with_nul("utf8 dash\0dash vs dash", "$dash\0$dash", $dash,
+ font => $font, channel => 1, utf8 => 1);
+ }
+
+ { # RT 11972
+ # when rendering to a transparent image the coverage should be
+ # expressed in terms of the alpha channel rather than the color
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ my $im = Imager->new(xsize => 40, ysize => 20, channels => 4);
+ ok($im->string(string => "AB", size => 20, aa => 1, color => '#F00',
+ x => 0, y => 15, font => $font),
+ "draw to transparent image");
+ my $im_noalpha = $im->convert(preset => 'noalpha');
+ my $im_pal = $im->to_paletted(make_colors => 'mediancut');
+ my @colors = $im_pal->getcolors;
+ is(@colors, 2, "should be only 2 colors");
+ @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
+ is_color3($colors[0], 0, 0, 0, "check we got black");
+ is_color3($colors[1], 255, 0, 0, "and red");
+ }
+
+ { # RT 27546
+ my $im = Imager->new(xsize => 100, ysize => 100, channels => 4);
+ $im->box(filled => 1, color => '#ff0000FF');
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ ok($im->string(x => 0, 'y' => 40, text => 'test',
+ size => 11, sizew => 11, font => $font, aa => 1),
+ 'draw on translucent image')
+ }
+
+ { # RT 60199
+ # not ft2 specific, but Imager
+ my $im = Imager->new(xsize => 100, ysize => 100);
+ my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
+ my $imcopy = $im->copy;
+ ok($im, "make test image");
+ ok($font, "make test font");
+ ok($im->align_string(valign => "center", halign => "center",
+ x => 50, y => 50, string => "0", color => "#FFF",
+ font => $font),
+ "draw 0 aligned");
+ ok(Imager::i_img_diff($im->{IMG}, $imcopy->{IMG}),
+ "make sure we drew the '0'");
+ }
+
+ SKIP:
+ { # RT 60509
+ # checks that a c:foo or c:\foo path is handled correctly on win32
+ my $type = "ft2";
+ $^O eq "MSWin32" || $^O eq "cygwin"
+ or skip("only for win32", 2);
+ my $dir = getcwd
+ or skip("Cannot get cwd", 2);
+ if ($^O eq "cygwin") {
+ $dir = Cygwin::posix_to_win_path($dir);
+ }
+ my $abs_path = abs_path($deffont);
+ my $font = Imager::Font->new(file => $abs_path, type => $type);
+ ok($font, "found font by absolute path")
+ or print "# path $abs_path\n";
+ undef $font;
+
+ $^O eq "cygwin"
+ and skip("cygwin doesn't support drive relative DOSsish paths", 1);
+ my ($drive) = $dir =~ /^([a-z]:)/i
+ or skip("cwd has no drive letter", 2);
+ my $drive_path = $drive . $deffont;
+ $font = Imager::Font->new(file => $drive_path, type => $type);
+ ok($font, "found font by drive relative path")
+ or print "# path $drive_path\n";
+ }
+
+}
+
+sub align_test {
+ my ($h, $v, $x, $y, $f, $img) = @_;
+
+ my @pos = $f->align(halign=>$h, valign=>$v, 'x'=>$x, 'y'=>$y,
+ image=>$img, size=>15, color=>'FFFFFF',
+ string=>"x$h ${v}y", channel=>1, aa=>1);
+ @pos = $img->align_string(halign=>$h, valign=>$v, 'x'=>$x, 'y'=>$y,
+ font=>$f, size=>15, color=>'FF99FF',
+ string=>"x$h ${v}y", aa=>1);
+ if (ok(@pos == 4, "$h $v aligned output")) {
+ # checking corners
+ my $cx = int(($pos[0] + $pos[2]) / 2);
+ my $cy = int(($pos[1] + $pos[3]) / 2);
+
+ print "# @pos cx $cx cy $cy\n";
+ okmatchcolor($img, $cx, $pos[1]-1, @base_color, "outer top edge");
+ okmatchcolor($img, $cx, $pos[3], @base_color, "outer bottom edge");
+ okmatchcolor($img, $pos[0]-1, $cy, @base_color, "outer left edge");
+ okmatchcolor($img, $pos[2], $cy, @base_color, "outer right edge");
+
+ okmismatchcolor($img, $cx, $pos[1], @base_color, "inner top edge");
+ okmismatchcolor($img, $cx, $pos[3]-1, @base_color, "inner bottom edge");
+ okmismatchcolor($img, $pos[0], $cy, @base_color, "inner left edge");
+# okmismatchcolor($img, $pos[2]-1, $cy, @base_color, "inner right edge");
+# XXX: This gets triggered by a freetype2 bug I think
+# $ rpm -qa | grep freetype
+# freetype-2.1.3-6
+#
+# (addi: 4/1/2004).
+
+ cross($img, $x, $y, 'FF0000');
+ cross($img, $cx, $pos[1]-1, '0000FF');
+ cross($img, $cx, $pos[3], '0000FF');
+ cross($img, $pos[0]-1, $cy, '0000FF');
+ cross($img, $pos[2], $cy, '0000FF');
+ }
+ else {
+ SKIP: { skip("couldn't draw text", 7) };
+ }
+}
+
+sub okmatchcolor {
+ my ($img, $x, $y, $r, $g, $b, $about) = @_;
+
+ my $c = $img->getpixel('x'=>$x, 'y'=>$y);
+ my ($fr, $fg, $fb) = $c->rgba;
+ ok($fr == $r && $fg == $g && $fb == $b,
+ "want ($r,$g,$b) found ($fr,$fg,$fb)\@($x,$y) $about");
+}
+
+sub okmismatchcolor {
+ my ($img, $x, $y, $r, $g, $b, $about) = @_;
+
+ my $c = $img->getpixel('x'=>$x, 'y'=>$y);
+ my ($fr, $fg, $fb) = $c->rgba;
+ ok($fr != $r || $fg != $g || $fb != $b,
+ "don't want ($r,$g,$b) found ($fr,$fg,$fb)\@($x,$y) $about");
+}
+
+sub cross {
+ my ($img, $x, $y, $color) = @_;
+
+ $img->setpixel('x'=>[$x, $x, $x, $x, $x, $x-2, $x-1, $x+1, $x+2],
+ 'y'=>[$y-2, $y-1, $y, $y+1, $y+2, $y, $y, $y, $y],
+ color => $color);
+
+}
--- /dev/null
+Imager::Font::FT2x T_PTROBJ
package Imager;
use strict;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS $warn_obsolete);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR %OPCODES $I2P $FORMATGUESS $warn_obsolete);
use IO::File;
use Imager::Color;
tiff => "Imager::File::TIFF",
jpeg => "Imager::File::JPEG",
w32 => "Imager::Font::W32",
+ ft2 => "Imager::Font::FT2",
);
tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
BEGIN {
- Imager::Font::__init();
for(i_list_formats()) { $formats_low{$_}++; }
- if (!$formats_low{'t1'} and !$formats_low{'tt'}
- && !$formats_low{'ft2'} && !$formats_low{'w32'}) {
- $fontstate='no font support';
- }
%OPCODES=(Add=>[0],Sub=>[1],Mult=>[2],Div=>[3],Parm=>[4],'sin'=>[5],'cos'=>[6],'x'=>[4,0],'y'=>[4,1]);
$DEBUG=0;
sub FETCH {
my ($self, $key) = @_;
- $DB::single = 1;
-
exists $self->[IX_FORMATS]{$key} and return $self->[IX_FORMATS]{$key};
$self->[IX_CLASSES]{$key} or return undef;
RETVAL
-#ifdef HAVE_FT2
-
-MODULE = Imager PACKAGE = Imager::Font::FT2 PREFIX=FT2_
-
-#define FT2_DESTROY(font) i_ft2_destroy(font)
-
-void
-FT2_DESTROY(font)
- Imager::Font::FT2 font
-
-int
-FT2_CLONE_SKIP(...)
- CODE:
- RETVAL = 1;
- OUTPUT:
- RETVAL
-
-MODULE = Imager PACKAGE = Imager::Font::FreeType2
-
-Imager::Font::FT2
-i_ft2_new(name, index)
- char *name
- int index
-
-undef_int
-i_ft2_setdpi(font, xdpi, ydpi)
- Imager::Font::FT2 font
- int xdpi
- int ydpi
-
-void
-i_ft2_getdpi(font)
- Imager::Font::FT2 font
- PREINIT:
- int xdpi, ydpi;
- CODE:
- if (i_ft2_getdpi(font, &xdpi, &ydpi)) {
- EXTEND(SP, 2);
- PUSHs(sv_2mortal(newSViv(xdpi)));
- PUSHs(sv_2mortal(newSViv(ydpi)));
- }
-
-undef_int
-i_ft2_sethinting(font, hinting)
- Imager::Font::FT2 font
- int hinting
-
-undef_int
-i_ft2_settransform(font, matrix)
- Imager::Font::FT2 font
- PREINIT:
- double matrix[6];
- int len;
- AV *av;
- SV *sv1;
- int i;
- CODE:
- if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
- croak("i_ft2_settransform: parameter 2 must be an array ref\n");
- av=(AV*)SvRV(ST(1));
- len=av_len(av)+1;
- if (len > 6)
- len = 6;
- for (i = 0; i < len; ++i) {
- sv1=(*(av_fetch(av,i,0)));
- matrix[i] = SvNV(sv1);
- }
- for (; i < 6; ++i)
- matrix[i] = 0;
- RETVAL = i_ft2_settransform(font, matrix);
- OUTPUT:
- RETVAL
-
-void
-i_ft2_bbox(font, cheight, cwidth, text_sv, utf8)
- Imager::Font::FT2 font
- double cheight
- double cwidth
- SV *text_sv
- int utf8
- PREINIT:
- int bbox[BOUNDING_BOX_COUNT];
- int i;
- char *text;
- STRLEN text_len;
- int rc;
- PPCODE:
- text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
- if (SvUTF8(text_sv))
- utf8 = 1;
-#endif
- rc = i_ft2_bbox(font, cheight, cwidth, text, text_len, bbox, utf8);
- if (rc) {
- EXTEND(SP, rc);
- for (i = 0; i < rc; ++i)
- PUSHs(sv_2mortal(newSViv(bbox[i])));
- }
-
-void
-i_ft2_bbox_r(font, cheight, cwidth, text, vlayout, utf8)
- Imager::Font::FT2 font
- double cheight
- double cwidth
- char *text
- int vlayout
- int utf8
- PREINIT:
- int bbox[8];
- int i;
- PPCODE:
-#ifdef SvUTF8
- if (SvUTF8(ST(3)))
- utf8 = 1;
-#endif
- if (i_ft2_bbox_r(font, cheight, cwidth, text, strlen(text), vlayout,
- utf8, bbox)) {
- EXTEND(SP, 8);
- for (i = 0; i < 8; ++i)
- PUSHs(sv_2mortal(newSViv(bbox[i])));
- }
-
-undef_int
-i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, align, aa, vlayout, utf8)
- Imager::Font::FT2 font
- Imager::ImgRaw im
- int tx
- int ty
- Imager::Color cl
- double cheight
- double cwidth
- int align
- int aa
- int vlayout
- int utf8
- PREINIT:
- char *text;
- STRLEN len;
- CODE:
-#ifdef SvUTF8
- if (SvUTF8(ST(7))) {
- utf8 = 1;
- }
-#endif
- text = SvPV(ST(7), len);
- RETVAL = i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text,
- len, align, aa, vlayout, utf8);
- OUTPUT:
- RETVAL
-
-undef_int
-i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text_sv, align, aa, vlayout, utf8)
- Imager::Font::FT2 font
- Imager::ImgRaw im
- int tx
- int ty
- int channel
- double cheight
- double cwidth
- SV *text_sv
- int align
- int aa
- int vlayout
- int utf8
- PREINIT:
- char const *text;
- STRLEN len;
- CODE:
-#ifdef SvUTF8
- if (SvUTF8(ST(7)))
- utf8 = 1;
-#endif
- text = SvPV(text_sv, len);
- RETVAL = i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text,
- len, align, aa, vlayout, 1);
- OUTPUT:
- RETVAL
-
-void
-ft2_transform_box(font, x0, x1, x2, x3)
- Imager::Font::FT2 font
- int x0
- int x1
- int x2
- int x3
- PREINIT:
- int box[4];
- PPCODE:
- box[0] = x0; box[1] = x1; box[2] = x2; box[3] = x3;
- ft2_transform_box(font, box);
- EXTEND(SP, 4);
- PUSHs(sv_2mortal(newSViv(box[0])));
- PUSHs(sv_2mortal(newSViv(box[1])));
- PUSHs(sv_2mortal(newSViv(box[2])));
- PUSHs(sv_2mortal(newSViv(box[3])));
-
-void
-i_ft2_has_chars(handle, text_sv, utf8)
- Imager::Font::FT2 handle
- SV *text_sv
- int utf8
- PREINIT:
- char *text;
- STRLEN len;
- char *work;
- int count;
- int i;
- PPCODE:
-#ifdef SvUTF8
- if (SvUTF8(text_sv))
- utf8 = 1;
-#endif
- text = SvPV(text_sv, len);
- work = mymalloc(len);
- count = i_ft2_has_chars(handle, text, len, utf8, work);
- if (GIMME_V == G_ARRAY) {
- EXTEND(SP, count);
- for (i = 0; i < count; ++i) {
- PUSHs(sv_2mortal(newSViv(work[i])));
- }
- }
- else {
- EXTEND(SP, 1);
- PUSHs(sv_2mortal(newSVpv(work, count)));
- }
- myfree(work);
-
-void
-i_ft2_face_name(handle)
- Imager::Font::FT2 handle
- PREINIT:
- char name[255];
- int len;
- PPCODE:
- len = i_ft2_face_name(handle, name, sizeof(name));
- if (len) {
- EXTEND(SP, 1);
- PUSHs(sv_2mortal(newSVpv(name, 0)));
- }
-
-undef_int
-i_ft2_can_face_name()
-
-void
-i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
- Imager::Font::FT2 handle
- SV *text_sv
- int utf8
- int reliable_only
- PREINIT:
- char const *text;
- STRLEN work_len;
- size_t len;
- char name[255];
- PPCODE:
-#ifdef SvUTF8
- if (SvUTF8(text_sv))
- utf8 = 1;
-#endif
- text = SvPV(text_sv, work_len);
- len = work_len;
- while (len) {
- unsigned long ch;
- if (utf8) {
- ch = i_utf8_advance(&text, &len);
- if (ch == ~0UL) {
- i_push_error(0, "invalid UTF8 character");
- break;
- }
- }
- else {
- ch = *text++;
- --len;
- }
- EXTEND(SP, 1);
- if (i_ft2_glyph_name(handle, ch, name, sizeof(name),
- reliable_only)) {
- PUSHs(sv_2mortal(newSVpv(name, 0)));
- }
- else {
- PUSHs(&PL_sv_undef);
- }
- }
-
-int
-i_ft2_can_do_glyph_names()
-
-int
-i_ft2_face_has_glyph_names(handle)
- Imager::Font::FT2 handle
-
-int
-i_ft2_is_multiple_master(handle)
- Imager::Font::FT2 handle
-
-void
-i_ft2_get_multiple_masters(handle)
- Imager::Font::FT2 handle
- PREINIT:
- i_font_mm mm;
- int i;
- PPCODE:
- if (i_ft2_get_multiple_masters(handle, &mm)) {
- EXTEND(SP, 2+mm.num_axis);
- PUSHs(sv_2mortal(newSViv(mm.num_axis)));
- PUSHs(sv_2mortal(newSViv(mm.num_designs)));
- for (i = 0; i < mm.num_axis; ++i) {
- AV *av = newAV();
- SV *sv;
- av_extend(av, 3);
- sv = newSVpv(mm.axis[i].name, strlen(mm.axis[i].name));
- SvREFCNT_inc(sv);
- av_store(av, 0, sv);
- sv = newSViv(mm.axis[i].minimum);
- SvREFCNT_inc(sv);
- av_store(av, 1, sv);
- sv = newSViv(mm.axis[i].maximum);
- SvREFCNT_inc(sv);
- av_store(av, 2, sv);
- PUSHs(newRV_noinc((SV *)av));
- }
- }
-
-undef_int
-i_ft2_set_mm_coords(handle, ...)
- Imager::Font::FT2 handle
- PROTOTYPE: DISABLE
- PREINIT:
- long *coords;
- int ix_coords, i;
- CODE:
- /* T_ARRAY handling by xsubpp seems to be busted in 5.6.1, so
- transfer the array manually */
- ix_coords = items-1;
- coords = mymalloc(sizeof(long) * ix_coords);
- for (i = 0; i < ix_coords; ++i) {
- coords[i] = (long)SvIV(ST(1+i));
- }
- RETVAL = i_ft2_set_mm_coords(handle, ix_coords, coords);
- myfree(coords);
- OUTPUT:
- RETVAL
-
-#endif
MODULE = Imager PACKAGE = Imager::FillHandle PREFIX=IFILL_
DynTest/Makefile.PL
DynTest/linstretch.c
DynTest/t/t00dyntest.t
+FT2/Makefile.PL
+FT2/FT2.pm
+FT2/FT2.xs
+FT2/freetyp2.c Implements freetype2 font support
+FT2/fontfiles/dodge.ttf
+FT2/fontfiles/ExistenceTest.afm
+FT2/fontfiles/ExistenceTest.pfb
+FT2/fontfiles/ExistenceTest.ttf
+FT2/fontfiles/ImUgly.ttf
+FT2/fontfiles/MMOne.pfb multiple master test font
+FT2/fontfiles/NameTest.ttf
+FT2/imft2.h
+FT2/typemap
Flines/Flines.pm
Flines/Flines.xs
Flines/Makefile.PL
fontfiles/ExistenceTest.pfb to change these files, edited and
fontfiles/ExistenceTest.ttf generated using pfaedit
fontfiles/ImUgly.ttf
-fontfiles/MMOne.pfb multiple master test font
-fontfiles/NameTest.ttf test glyph_names() - see t38ft2font.t
+fontfiles/NameTest.ttf test glyph_names() - see FT2/t/t10ft2.t
fontfiles/SpaceTest.afm
fontfiles/SpaceTest.pfb test bounding box with spaces in t30t1font.t
fontfiles/dcr10.afm
fontfiles/dcr10.pfb
fontfiles/dodge.ttf
-freetyp2.c Implements freetype2 font support
gaussian.im
hlines.c Manage sets of horizontal line segments
image.c
t/t30t1font.t
t/t35ttfont.t
t/t36oofont.t
-t/t38ft2font.t Tests freetype2 support
t/t40scale.t
t/t50basicoo.t
t/t55trans.t
# This currently only works on under normal Win32 and cygwin.
# DOCS
# };
- $formats{'freetype2'} =
- {
- order=>'29',
- def=>'HAVE_FT2',
- # we always use a probe function
- #inccheck=>sub { -e catfile($_[0], 'ft2build.h') },
- #libcheck=>sub { $_[0] eq "libfreetype$aext" or $_[0] eq "libfreetype.$lext" },
- libfiles=>'-lfreetype',
- objfiles=>'freetyp2.o',
- docs=><<DOCS,
-Freetype 2 supports both Truetype and Type 1 fonts, both of which are
-scalable. It also supports a variety of other fonts.
-DOCS
- code =>
- [
- \&freetype2_probe_ftconfig,
- \&freetype2_probe_scan
- ],
- };
+# $formats{'freetype2'} =
+# {
+# order=>'29',
+# def=>'HAVE_FT2',
+# # we always use a probe function
+# #inccheck=>sub { -e catfile($_[0], 'ft2build.h') },
+# #libcheck=>sub { $_[0] eq "libfreetype$aext" or $_[0] eq "libfreetype.$lext" },
+# libfiles=>'-lfreetype',
+# objfiles=>'freetyp2.o',
+# docs=><<DOCS,
+# Freetype 2 supports both Truetype and Type 1 fonts, both of which are
+# scalable. It also supports a variety of other fonts.
+# DOCS
+# code =>
+# [
+# \&freetype2_probe_ftconfig,
+# \&freetype2_probe_scan
+# ],
+# };
# Make fix indent
for (keys %formats) { $formats{$_}->{docs} =~ s/^\s+/ /mg; }
close CONFIG;
}
-# use pkg-config to probe for libraries
-# works around the junk that pkg-config dumps on FreeBSD
-sub _pkg_probe {
- my ($pkg) = @_;
+# # use pkg-config to probe for libraries
+# # works around the junk that pkg-config dumps on FreeBSD
+# sub _pkg_probe {
+# my ($pkg) = @_;
- is_exe('pkg-config') or return;
+# is_exe('pkg-config') or return;
- my $redir = $^O eq 'MSWin32' ? '' : '2>/dev/null';
+# my $redir = $^O eq 'MSWin32' ? '' : '2>/dev/null';
- !system("pkg-config $pkg --exists $redir");
-}
+# !system("pkg-config $pkg --exists $redir");
+# }
# probes for freetype1 by scanning @incs for the includes and
# @libs for the libs. This is done separately because freetype's headers
}
# probes for freetype2 by trying to run freetype-config
-sub freetype2_probe_ftconfig {
- my ($frm, $frmkey) = @_;
-
- is_exe('freetype-config') or return;
-
- my $cflags = `freetype-config --cflags`
- and !$? or return;
- chomp $cflags;
-
- my $lflags = `freetype-config --libs`
- and !$? or return;
- chomp $lflags;
-
- # before 2.1.5 freetype-config --cflags could output
- # the -I options in the wrong order, causing a conflict with
- # freetype1.x installed with the same --prefix
- #
- # can happen iff:
- # - both -Iprefix/include and -Iprefix/include/freetype2 are in cflags
- # in that order
- # - freetype 1.x headers are in prefix/include/freetype
- my @incdirs = map substr($_, 2), grep /^-I/, split ' ', $cflags;
- if (@incdirs == 2
- && $incdirs[1] eq "$incdirs[0]/freetype2"
- && -e "$incdirs[0]/freetype/freetype.h"
- && -e "$incdirs[0]/freetype/fterrid.h") {
- print "** freetype-config provided -I options out of order, correcting\n"
- if $VERBOSE;
- $cflags = join(' ', grep(!/-I/, split ' ', $cflags),
- map "-I$_", reverse @incdirs);
- }
- $frm->{cflags} = $cflags;
- $frm->{lflags} = $lflags;
+# sub freetype2_probe_ftconfig {
+# my ($frm, $frmkey) = @_;
+
+# is_exe('freetype-config') or return;
+
+# my $cflags = `freetype-config --cflags`
+# and !$? or return;
+# chomp $cflags;
+
+# my $lflags = `freetype-config --libs`
+# and !$? or return;
+# chomp $lflags;
+
+# # before 2.1.5 freetype-config --cflags could output
+# # the -I options in the wrong order, causing a conflict with
+# # freetype1.x installed with the same --prefix
+# #
+# # can happen iff:
+# # - both -Iprefix/include and -Iprefix/include/freetype2 are in cflags
+# # in that order
+# # - freetype 1.x headers are in prefix/include/freetype
+# my @incdirs = map substr($_, 2), grep /^-I/, split ' ', $cflags;
+# if (@incdirs == 2
+# && $incdirs[1] eq "$incdirs[0]/freetype2"
+# && -e "$incdirs[0]/freetype/freetype.h"
+# && -e "$incdirs[0]/freetype/fterrid.h") {
+# print "** freetype-config provided -I options out of order, correcting\n"
+# if $VERBOSE;
+# $cflags = join(' ', grep(!/-I/, split ' ', $cflags),
+# map "-I$_", reverse @incdirs);
+# }
+# $frm->{cflags} = $cflags;
+# $frm->{lflags} = $lflags;
- printf "%10s: configured via freetype-config\n", $frmkey;
+# printf "%10s: configured via freetype-config\n", $frmkey;
- return 1;
-}
+# return 1;
+# }
# attempt to probe for freetype2 by scanning directories
# we can't use the normal scan since we need to find the directory
# containing most of the includes
-sub freetype2_probe_scan {
- my ($frm, $frmkey) = @_;
-
- my $found_inc;
- my $found_inc2;
- INCS:
- for my $inc (@incs) {
- my $path = File::Spec->catfile($inc, 'ft2build.h');
- -e $path or next;
-
- # try to find what it's including
- my $ftheader;
- open FT2BUILD, "< $path"
- or next;
- while (<FT2BUILD>) {
- if (m!^\s*\#\s*include\s+<([\w/.]+)>!
- || m!^\s*\#\s*include\s+"([\w/.]+)"!) {
- $ftheader = $1;
- last;
- }
- }
- close FT2BUILD;
- $ftheader
- or next;
- # non-Unix installs put this directly under the same directory in
- # theory
- if (-e File::Spec->catfile($inc, $ftheader)) {
- $found_inc = $inc;
- last INCS;
- }
- for my $subdir (qw/freetype2 freetype/) {
- $path = File::Spec->catfile($inc, $subdir, 'fterrors.h');
- -e $path and next;
+# sub freetype2_probe_scan {
+# my ($frm, $frmkey) = @_;
+
+# my $found_inc;
+# my $found_inc2;
+# INCS:
+# for my $inc (@incs) {
+# my $path = File::Spec->catfile($inc, 'ft2build.h');
+# -e $path or next;
+
+# # try to find what it's including
+# my $ftheader;
+# open FT2BUILD, "< $path"
+# or next;
+# while (<FT2BUILD>) {
+# if (m!^\s*\#\s*include\s+<([\w/.]+)>!
+# || m!^\s*\#\s*include\s+"([\w/.]+)"!) {
+# $ftheader = $1;
+# last;
+# }
+# }
+# close FT2BUILD;
+# $ftheader
+# or next;
+# # non-Unix installs put this directly under the same directory in
+# # theory
+# if (-e File::Spec->catfile($inc, $ftheader)) {
+# $found_inc = $inc;
+# last INCS;
+# }
+# for my $subdir (qw/freetype2 freetype/) {
+# $path = File::Spec->catfile($inc, $subdir, 'fterrors.h');
+# -e $path and next;
- $found_inc = $inc;
- $found_inc2 = File::Spec->catdir($inc, $subdir);
- last INCS;
- }
- }
+# $found_inc = $inc;
+# $found_inc2 = File::Spec->catdir($inc, $subdir);
+# last INCS;
+# }
+# }
- my $found_lib;
- LIBS:
- for my $lib (@libs) {
- my $a_path = File::Spec->catfile($lib, "libfreetype$aext");
- my $l_path = File::Spec->catfile($lib, "libfreetype.$lext");
- if (-e $a_path || -e $l_path) {
- $found_lib = $lib;
- last LIBS;
- }
- }
+# my $found_lib;
+# LIBS:
+# for my $lib (@libs) {
+# my $a_path = File::Spec->catfile($lib, "libfreetype$aext");
+# my $l_path = File::Spec->catfile($lib, "libfreetype.$lext");
+# if (-e $a_path || -e $l_path) {
+# $found_lib = $lib;
+# last LIBS;
+# }
+# }
- printf("%10s: includes %s - libraries %s\n", $frmkey,
- ($found_inc ? 'found' : 'not found'),
- ($found_lib ? 'found' : 'not found'));
+# printf("%10s: includes %s - libraries %s\n", $frmkey,
+# ($found_inc ? 'found' : 'not found'),
+# ($found_lib ? 'found' : 'not found'));
- return unless $found_inc && $found_lib;
+# return unless $found_inc && $found_lib;
- $frm->{cflags} = _make_I($found_inc);
- $frm->{cflags} .= " " . _make_I($found_inc2) if $found_inc2;
- $frm->{libfiles} = "-lfreetype";
+# $frm->{cflags} = _make_I($found_inc);
+# $frm->{cflags} .= " " . _make_I($found_inc2) if $found_inc2;
+# $frm->{libfiles} = "-lfreetype";
- return 1;
-}
+# return 1;
+# }
sub _make_I {
my ($inc_dir) = @_;
}
# probes for libpng via pkg-config
-sub png_probe {
- my ($frm, $frmkey) = @_;
+# sub png_probe {
+# my ($frm, $frmkey) = @_;
- is_exe('pkg-config') or return;
+# is_exe('pkg-config') or return;
- my $config;
- for my $check_conf (qw(libpng libpng12 libpng10)) {
- if (_pkg_probe($check_conf)) {
- $config = $check_conf;
- last;
- }
- }
- $config or return;
+# my $config;
+# for my $check_conf (qw(libpng libpng12 libpng10)) {
+# if (_pkg_probe($check_conf)) {
+# $config = $check_conf;
+# last;
+# }
+# }
+# $config or return;
- my $cflags = `pkg-config $config --cflags`
- and !$? or return;
+# my $cflags = `pkg-config $config --cflags`
+# and !$? or return;
- my $lflags = `pkg-config $config --libs`
- and !$? or return;
+# my $lflags = `pkg-config $config --libs`
+# and !$? or return;
- chomp $cflags;
- chomp $lflags;
- $frm->{cflags} = $cflags;
- $frm->{lflags} = $lflags;
+# chomp $cflags;
+# chomp $lflags;
+# $frm->{cflags} = $cflags;
+# $frm->{lflags} = $lflags;
- printf "%10s: configured via `pkg-config $config ...`\n", $frmkey;
+# printf "%10s: configured via `pkg-config $config ...`\n", $frmkey;
- return 1;
-}
+# return 1;
+# }
sub catfile {
return File::Spec->catfile(@_);
}
-sub is_exe {
- my ($name) = @_;
+# sub is_exe {
+# my ($name) = @_;
- my @exe_suffix = $Config{_exe};
- if ($^O eq 'MSWin32') {
- push @exe_suffix, qw/.bat .cmd/;
- }
+# my @exe_suffix = $Config{_exe};
+# if ($^O eq 'MSWin32') {
+# push @exe_suffix, qw/.bat .cmd/;
+# }
- for my $dir (File::Spec->path) {
- for my $suffix (@exe_suffix) {
- -x catfile($dir, "$name$suffix")
- and return 1;
- }
- }
+# for my $dir (File::Spec->path) {
+# for my $suffix (@exe_suffix) {
+# -x catfile($dir, "$name$suffix")
+# and return 1;
+# }
+# }
- return;
-}
+# return;
+# }
sub usage {
print STDERR <<EOS;
print OUT "\n=back\n\n";
}
-# see if we have an uncategorized section
+# see if we have an uncategorised section
if (grep $alldocs{$_}, keys %undoc) {
print OUT "=head2 Uncategorized functions\n\n=over\n\n";
#print join(",", grep !exists $order{$_}, @funcs), "\n";
i_fcolor fc;
} i_fill_solid_t;
-static void fill_solid(i_fill_t *, int x, int y, int width, int channels,
- i_color *);
-static void fill_solidf(i_fill_t *, int x, int y, int width, int channels,
- i_fcolor *);
+static void fill_solid(i_fill_t *, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_color *);
+static void fill_solidf(i_fill_t *, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *);
static i_fill_solid_t base_solid_fill =
{
int dx, dy;
} i_fill_hatch_t;
-static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data);
-static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+static void fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_color *data);
+static void fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_fcolor *data);
static
i_fill_t *
i_new_hatch_low(const i_color *fg, const i_color *bg, const i_fcolor *ffg, const i_fcolor *fbg,
dx, dy);
}
-static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data);
-static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+static void fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_color *data);
+static void fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_fcolor *data);
struct i_fill_image_t {
i_fill_t base;
i_img *src;
return &fill->base;
}
-static void fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data);
-static void fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data);
+static void fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_color *data);
+static void fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y,
+ i_img_dim width, int channels, i_fcolor *data);
struct i_fill_opacity_t {
i_fill_t base;
=cut
*/
static void
-fill_solid(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+fill_solid(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_color *data) {
i_color c = T_SOLID_FILL(fill)->c;
i_adapt_colors(channels > 2 ? 4 : 2, 4, &c, 1);
while (width-- > 0) {
=cut
*/
static void
-fill_solidf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+fill_solidf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *data) {
i_fcolor c = T_SOLID_FILL(fill)->fc;
i_adapt_fcolors(channels > 2 ? 4 : 2, 4, &c, 1);
while (width-- > 0) {
=cut
*/
-static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+static void
+fill_hatch(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_color *data) {
i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
int byte = f->hatch[(y + f->dy) & 7];
int xpos = (x + f->dx) & 7;
=back
*/
-static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+static void
+fill_hatchf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *data) {
i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
int byte = f->hatch[(y + f->dy) & 7];
int xpos = (x + f->dx) & 7;
=cut
*/
-static void fill_image(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+static void
+fill_image(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_color *data) {
struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
- int i = 0;
+ i_img_dim i = 0;
i_color *out = data;
int want_channels = channels > 2 ? 4 : 2;
double iy = floor(ry / f->src->ysize);
i_color c[2][2];
i_color c2[2];
- int dy;
+ i_img_dim dy;
if (f->xoff) {
rx += iy * f->xoff;
/* the easy way */
/* this should be possible to optimize to use i_glin() */
while (i < width) {
- int rx = x+i;
- int ry = y;
- int ix = rx / f->src->xsize;
- int iy = ry / f->src->ysize;
+ i_img_dim rx = x+i;
+ i_img_dim ry = y;
+ i_img_dim ix = rx / f->src->xsize;
+ i_img_dim iy = ry / f->src->ysize;
if (f->xoff) {
rx += iy * f->xoff;
=cut
*/
-static void fill_imagef(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+static void
+fill_imagef(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *data) {
struct i_fill_image_t *f = (struct i_fill_image_t *)fill;
- int i = 0;
+ i_img_dim i = 0;
int want_channels = channels > 2 ? 4 : 2;
if (f->has_matrix) {
double iy = floor(ry / f->src->ysize);
i_fcolor c[2][2];
i_fcolor c2[2];
- int dy;
+ i_img_dim dy;
if (f->xoff) {
rx += iy * f->xoff;
/* the easy way */
/* this should be possible to optimize to use i_glin() */
while (i < width) {
- int rx = x+i;
- int ry = y;
- int ix = rx / f->src->xsize;
- int iy = ry / f->src->ysize;
+ i_img_dim rx = x+i;
+ i_img_dim ry = y;
+ i_img_dim ix = rx / f->src->xsize;
+ i_img_dim iy = ry / f->src->ysize;
if (f->xoff) {
rx += iy * f->xoff;
}
static void
-fill_opacity(i_fill_t *fill, int x, int y, int width, int channels,
- i_color *data) {
+fill_opacity(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_color *data) {
struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
int alpha_chan = channels > 2 ? 3 : 1;
i_color *datap = data;
}
}
static void
-fill_opacityf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+fill_opacityf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *data) {
struct i_fill_opacity_t *f = (struct i_fill_opacity_t *)fill;
int alpha_chan = channels > 2 ? 3 : 1;
i_fcolor *datap = data;
} i_fill_fountain_t;
static void
-fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
+fill_fountf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width, int channels,
i_fcolor *data);
static void
fount_fill_destroy(i_fill_t *fill);
=cut
*/
static void
-fill_fountf(i_fill_t *fill, int x, int y, int width, int channels,
- i_fcolor *data) {
+fill_fountf(i_fill_t *fill, i_img_dim x, i_img_dim y, i_img_dim width,
+ int channels, i_fcolor *data) {
i_fill_fountain_t *f = (i_fill_fountain_t *)fill;
while (width--) {
+++ /dev/null
-/*
-=head1 NAME
-
-freetyp2.c - font support via the FreeType library version 2.
-
-=head1 SYNOPSIS
-
- if (!i_ft2_init()) { error }
- FT2_Fonthandle *font;
- font = i_ft2_new(name, index);
- if (!i_ft2_setdpi(font, xdpi, ydpi)) { error }
- if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
- double matrix[6];
- if (!i_ft2_settransform(font, matrix)) { error }
- int bbox[BOUNDING_BOX_COUNT];
- if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
- i_img *im = ...;
- i_color cl;
- if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
- aa)) { error }
- if (!i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, length,
- align, aa)) { error }
- i_ft2_destroy(font);
-
-=head1 DESCRIPTION
-
-Implements Imager font support using the FreeType2 library.
-
-The FreeType2 library understands several font file types, including
-Truetype, Type1 and Windows FNT.
-
-=over
-
-=cut
-*/
-
-#include "imager.h"
-#include <stdio.h>
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#ifdef FT_MULTIPLE_MASTERS_H
-#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
-#define IM_FT2_MM
-#include FT_MULTIPLE_MASTERS_H
-#endif
-#endif
-
-static void ft2_push_message(int code);
-
-static int ft2_initialized = 0;
-static FT_Library library;
-
-/*
-=item i_ft2_init(void)
-
-Initializes the Freetype 2 library.
-
-Returns true on success, false on failure.
-
-=cut
-*/
-int
-i_ft2_init(void) {
- FT_Error error;
-
- i_clear_error();
- error = FT_Init_FreeType(&library);
- if (error) {
- ft2_push_message(error);
- i_push_error(0, "Initializing Freetype2");
- return 0;
- }
-
- ft2_initialized = 1;
-
- return 1;
-}
-
-struct FT2_Fonthandle {
- FT_Face face;
- int xdpi, ydpi;
- int hint;
- FT_Encoding encoding;
-
- /* used to adjust so we can align the draw point to the top-left */
- double matrix[6];
-
-#ifdef IM_FT2_MM
- /* Multiple master data if any */
- int has_mm;
- FT_Multi_Master mm;
-#endif
-};
-
-/* the following is used to select a "best" encoding */
-static struct enc_score {
- FT_Encoding encoding;
- int score;
-} enc_scores[] =
-{
- /* the selections here are fairly arbitrary
- ideally we need to give the user a list of encodings available
- and a mechanism to choose one */
- { ft_encoding_unicode, 10 },
- { ft_encoding_sjis, 8 },
- { ft_encoding_gb2312, 8 },
- { ft_encoding_big5, 8 },
- { ft_encoding_wansung, 8 },
- { ft_encoding_johab, 8 },
- { ft_encoding_latin_2, 6 },
- { ft_encoding_apple_roman, 6 },
- { ft_encoding_adobe_standard, 6 },
- { ft_encoding_adobe_expert, 6 },
-};
-
-/*
-=item i_ft2_new(char *name, int index)
-
-Creates a new font object, from the file given by I<name>. I<index>
-is the index of the font in a file with multiple fonts, where 0 is the
-first font.
-
-Return NULL on failure.
-
-=cut
-*/
-
-FT2_Fonthandle *
-i_ft2_new(const char *name, int index) {
- FT_Error error;
- FT2_Fonthandle *result;
- FT_Face face;
- int i, j;
- FT_Encoding encoding;
- int score;
-
- mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index));
-
- if (!ft2_initialized && !i_ft2_init())
- return NULL;
-
- i_clear_error();
- error = FT_New_Face(library, name, index, &face);
- if (error) {
- ft2_push_message(error);
- i_push_error(error, "Opening face");
- mm_log((2, "error opening face '%s': %d\n", name, error));
- return NULL;
- }
-
- encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode;
- score = 0;
- for (i = 0; i < face->num_charmaps; ++i) {
- FT_Encoding enc_entry = face->charmaps[i]->encoding;
- mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n",
- enc_entry, face->charmaps[i]->platform_id,
- face->charmaps[i]->encoding_id));
- for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) {
- if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) {
- encoding = enc_entry;
- score = enc_scores[j].score;
- break;
- }
- }
- }
- FT_Select_Charmap(face, encoding);
- mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding));
-
- result = mymalloc(sizeof(FT2_Fonthandle));
- result->face = face;
- result->xdpi = result->ydpi = 72;
- result->encoding = encoding;
-
- /* by default we disable hinting on a call to i_ft2_settransform()
- if we don't do this, then the hinting can the untransformed text
- to be a different size to the transformed text.
- Obviously we have it initially enabled.
- */
- result->hint = 1;
-
- /* I originally forgot this: :/ */
- /*i_ft2_settransform(result, matrix); */
- result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
- result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
-
-#ifdef IM_FT2_MM
- {
- FT_Multi_Master *mm = &result->mm;
- int i;
-
- if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0
- && (error = FT_Get_Multi_Master(face, mm)) == 0) {
- mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
- for (i = 0; i < mm->num_axis; ++i) {
- mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
- (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
- }
- result->has_mm = 1;
- }
- else {
- mm_log((2, "No multiple masters\n"));
- result->has_mm = 0;
- }
- }
-#endif
-
- return result;
-}
-
-/*
-=item i_ft2_destroy(FT2_Fonthandle *handle)
-
-Destroys a font object, which must have been the return value of
-i_ft2_new().
-
-=cut
-*/
-void
-i_ft2_destroy(FT2_Fonthandle *handle) {
- FT_Done_Face(handle->face);
- myfree(handle);
-}
-
-/*
-=item i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi)
-
-Sets the resolution in dots per inch at which point sizes scaled, by
-default xdpi and ydpi are 72, so that 1 point maps to 1 pixel.
-
-Both xdpi and ydpi should be positive.
-
-Return true on success.
-
-=cut
-*/
-int
-i_ft2_setdpi(FT2_Fonthandle *handle, int xdpi, int ydpi) {
- i_clear_error();
- if (xdpi > 0 && ydpi > 0) {
- handle->xdpi = xdpi;
- handle->ydpi = ydpi;
- return 0;
- }
- else {
- i_push_error(0, "resolutions must be positive");
- return 0;
- }
-}
-
-/*
-=item i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi)
-
-Retrieves the current horizontal and vertical resolutions at which
-point sizes are scaled.
-
-=cut
-*/
-int
-i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi) {
- *xdpi = handle->xdpi;
- *ydpi = handle->ydpi;
-
- return 1;
-}
-
-/*
-=item i_ft2_settransform(FT2_FontHandle *handle, double *matrix)
-
-Sets a transormation matrix for output.
-
-This should be a 2 x 3 matrix like:
-
- matrix[0] matrix[1] matrix[2]
- matrix[3] matrix[4] matrix[5]
-
-=cut
-*/
-int
-i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix) {
- FT_Matrix m;
- FT_Vector v;
- int i;
-
- m.xx = matrix[0] * 65536;
- m.xy = matrix[1] * 65536;
- v.x = matrix[2]; /* this could be pels of 26.6 fixed - not sure */
- m.yx = matrix[3] * 65536;
- m.yy = matrix[4] * 65536;
- v.y = matrix[5]; /* see just above */
-
- FT_Set_Transform(handle->face, &m, &v);
-
- for (i = 0; i < 6; ++i)
- handle->matrix[i] = matrix[i];
- handle->hint = 0;
-
- return 1;
-}
-
-/*
-=item i_ft2_sethinting(FT2_Fonthandle *handle, int hinting)
-
-If hinting is non-zero then glyph hinting is enabled, otherwise disabled.
-
-i_ft2_settransform() disables hinting to prevent distortions in
-gradual text transformations.
-
-=cut
-*/
-int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
- handle->hint = hinting;
- return 1;
-}
-
-/*
-=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int *bbox)
-
-Retrieves bounding box information for the font at the given
-character width and height. This ignores the transformation matrix.
-
-Returns non-zero on success.
-
-=cut
-*/
-int
-i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
- char const *text, size_t len, int *bbox, int utf8) {
- FT_Error error;
- int width;
- int index;
- int first;
- int ascent = 0, descent = 0;
- int glyph_ascent, glyph_descent;
- FT_Glyph_Metrics *gm;
- int start = 0;
- int loadFlags = FT_LOAD_DEFAULT;
- int rightb = 0;
-
- mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
- handle, cheight, cwidth, text, len, bbox));
-
- error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
- handle->xdpi, handle->ydpi);
- if (error) {
- ft2_push_message(error);
- i_push_error(0, "setting size");
- }
-
- if (!handle->hint)
- loadFlags |= FT_LOAD_NO_HINTING;
-
- first = 1;
- width = 0;
- while (len) {
- unsigned long c;
- 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;
- }
-
- index = FT_Get_Char_Index(handle->face, c);
- error = FT_Load_Glyph(handle->face, index, loadFlags);
- if (error) {
- ft2_push_message(error);
- i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
- c, index);
- return 0;
- }
- gm = &handle->face->glyph->metrics;
- glyph_ascent = gm->horiBearingY / 64;
- glyph_descent = glyph_ascent - gm->height/64;
- if (first) {
- start = gm->horiBearingX / 64;
- /* handles -ve values properly */
- ascent = glyph_ascent;
- descent = glyph_descent;
- first = 0;
- }
-
- if (glyph_ascent > ascent)
- ascent = glyph_ascent;
- if (glyph_descent < descent)
- descent = glyph_descent;
-
- width += gm->horiAdvance / 64;
-
- if (len == 0) {
- /* last character
- handle the case where the right the of the character overlaps the
- right*/
- rightb = (gm->horiAdvance - gm->horiBearingX - gm->width)/64;
- /*if (rightb > 0)
- rightb = 0;*/
- }
- }
-
- bbox[BBOX_NEG_WIDTH] = start;
- bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
- bbox[BBOX_POS_WIDTH] = width;
- if (rightb < 0)
- bbox[BBOX_POS_WIDTH] -= rightb;
- bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
- bbox[BBOX_DESCENT] = descent;
- bbox[BBOX_ASCENT] = ascent;
- bbox[BBOX_ADVANCE_WIDTH] = width;
- bbox[BBOX_RIGHT_BEARING] = rightb;
- mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
-
- return BBOX_RIGHT_BEARING + 1;
-}
-
-/*
-=item transform_box(FT2_FontHandle *handle, int bbox[4])
-
-bbox contains coorinates of a the top-left and bottom-right of a bounding
-box relative to a point.
-
-This is then transformed and the values in bbox[4] are the top-left
-and bottom-right of the new bounding box.
-
-This is meant to provide the bounding box of a transformed character
-box. The problem is that if the character was round and is rotated,
-the real bounding box isn't going to be much different from the
-original, but this function will return a _bigger_ bounding box. I
-suppose I could work my way through the glyph outline, but that's
-too much hard work.
-
-=cut
-*/
-void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) {
- double work[8];
- double *matrix = handle->matrix;
-
- work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1];
- work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1];
- work[2] = matrix[0] * bbox[2] + matrix[1] * bbox[1];
- work[3] = matrix[3] * bbox[2] + matrix[4] * bbox[1];
- work[4] = matrix[0] * bbox[0] + matrix[1] * bbox[3];
- work[5] = matrix[3] * bbox[0] + matrix[4] * bbox[3];
- work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3];
- work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3];
-
- bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6])));
- bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7])));
- bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6])));
- bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7])));
-}
-
-/*
-=item expand_bounds(int bbox[4], int bbox2[4])
-
-Treating bbox[] and bbox2[] as 2 bounding boxes, produces a new
-bounding box in bbox[] that encloses both.
-
-=cut
-*/
-static void expand_bounds(int bbox[4], int bbox2[4]) {
- bbox[0] = i_min(bbox[0], bbox2[0]);
- bbox[1] = i_min(bbox[1], bbox2[1]);
- bbox[2] = i_max(bbox[2], bbox2[2]);
- bbox[3] = i_max(bbox[3], bbox2[3]);
-}
-
-/*
-=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, int *bbox)
-
-Retrieves bounding box information for the font at the given
-character width and height.
-
-This version finds the rectangular bounding box of the glyphs, with
-the text as transformed by the transformation matrix. As with
-i_ft2_bbox (bbox[0], bbox[1]) will the the offset from the start of
-the topline to the top-left of the bounding box. Unlike i_ft2_bbox()
-this could be near the bottom left corner of the box.
-
-(bbox[4], bbox[5]) is the offset to the start of the baseline.
-(bbox[6], bbox[7]) is the offset from the start of the baseline to the
-end of the baseline.
-
-Returns non-zero on success.
-
-=cut
-*/
-int
-i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
- char const *text, size_t len, int vlayout, int utf8, int *bbox) {
- FT_Error error;
- int width;
- int index;
- int first;
- int ascent = 0, descent = 0;
- int glyph_ascent, glyph_descent;
- FT_Glyph_Metrics *gm;
- int work[4];
- int bounds[4];
- double x = 0, y = 0;
- int i;
- FT_GlyphSlot slot;
- int loadFlags = FT_LOAD_DEFAULT;
-
- if (vlayout)
- loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
- if (!handle->hint)
- loadFlags |= FT_LOAD_NO_HINTING;
-
- error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
- handle->xdpi, handle->ydpi);
- if (error) {
- ft2_push_message(error);
- i_push_error(0, "setting size");
- }
-
- first = 1;
- width = 0;
- while (len) {
- unsigned long c;
- 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;
- }
-
- index = FT_Get_Char_Index(handle->face, c);
- error = FT_Load_Glyph(handle->face, index, loadFlags);
- if (error) {
- ft2_push_message(error);
- i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
- c, index);
- return 0;
- }
- slot = handle->face->glyph;
- gm = &slot->metrics;
-
- /* these probably don't mean much for vertical layouts */
- glyph_ascent = gm->horiBearingY / 64;
- glyph_descent = glyph_ascent - gm->height/64;
- if (vlayout) {
- work[0] = gm->vertBearingX;
- work[1] = gm->vertBearingY;
- }
- else {
- work[0] = gm->horiBearingX;
- work[1] = gm->horiBearingY;
- }
- work[2] = gm->width + work[0];
- work[3] = work[1] - gm->height;
- if (first) {
- bbox[4] = work[0] * handle->matrix[0] + work[1] * handle->matrix[1] + handle->matrix[2];
- bbox[5] = work[0] * handle->matrix[3] + work[1] * handle->matrix[4] + handle->matrix[5];
- bbox[4] = bbox[4] < 0 ? -(-bbox[4] + 32)/64 : (bbox[4] + 32) / 64;
- bbox[5] /= 64;
- }
- ft2_transform_box(handle, work);
- for (i = 0; i < 4; ++i)
- work[i] /= 64;
- work[0] += x;
- work[1] += y;
- work[2] += x;
- work[3] += y;
- if (first) {
- for (i = 0; i < 4; ++i)
- bounds[i] = work[i];
- ascent = glyph_ascent;
- descent = glyph_descent;
- first = 0;
- }
- else {
- expand_bounds(bounds, work);
- }
- x += slot->advance.x / 64;
- y += slot->advance.y / 64;
-
- if (glyph_ascent > ascent)
- ascent = glyph_ascent;
- if (glyph_descent > descent)
- descent = glyph_descent;
-
- if (len == 0) {
- /* last character
- handle the case where the right the of the character overlaps the
- right*/
- /*int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
- if (rightb < 0)
- width -= rightb / 64;*/
- }
- }
-
- /* at this point bounds contains the bounds relative to the CP,
- and x, y hold the final position relative to the CP */
- /*bounds[0] -= x;
- bounds[1] -= y;
- bounds[2] -= x;
- bounds[3] -= y;*/
-
- bbox[0] = bounds[0];
- bbox[1] = -bounds[3];
- bbox[2] = bounds[2];
- bbox[3] = -bounds[1];
- bbox[6] = x;
- bbox[7] = -y;
-
- return 1;
-}
-
-static int
-make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
-
-/*
-=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
-
-Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given
-I<cheight> and I<cwidth>.
-
-If align is 0, then the text is rendered with the top-left of the
-first character at (I<tx>, I<ty>). If align is non-zero then the text
-is rendered with (I<tx>, I<ty>) aligned with the base-line of the
-characters.
-
-If aa is non-zero then the text is anti-aliased.
-
-Returns non-zero on success.
-
-=cut
-*/
-int
-i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
- double cheight, double cwidth, char const *text, size_t len,
- int align, int aa, int vlayout, int utf8) {
- FT_Error error;
- int index;
- FT_Glyph_Metrics *gm;
- int bbox[BOUNDING_BOX_COUNT];
- FT_GlyphSlot slot;
- int x, y;
- unsigned char *bmp;
- unsigned char map[256];
- char last_mode = ft_pixel_mode_none;
- int last_grays = -1;
- int loadFlags = FT_LOAD_DEFAULT;
- i_render render;
-
- mm_log((1, "i_ft2_text(handle %p, im %p, tx %d, ty %d, cl %p, cheight %f, cwidth %f, text %p, len %d, align %d, aa %d)\n",
- handle, im, tx, ty, cl, cheight, cwidth, text, align, aa));
-
- if (vlayout) {
- if (!FT_HAS_VERTICAL(handle->face)) {
- i_push_error(0, "face has no vertical metrics");
- return 0;
- }
- loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
- }
- if (!handle->hint)
- loadFlags |= FT_LOAD_NO_HINTING;
-
- /* set the base-line based on the string ascent */
- if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
- return 0;
-
- if (aa)
- i_render_init(&render, im, bbox[BBOX_POS_WIDTH] - bbox[BBOX_NEG_WIDTH]);
-
- if (!align) {
- /* this may need adjustment */
- tx -= bbox[0] * handle->matrix[0] + bbox[5] * handle->matrix[1] + handle->matrix[2];
- ty += bbox[0] * handle->matrix[3] + bbox[5] * handle->matrix[4] + handle->matrix[5];
- }
- while (len) {
- unsigned long c;
- 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;
- }
-
- index = FT_Get_Char_Index(handle->face, c);
- error = FT_Load_Glyph(handle->face, index, loadFlags);
- if (error) {
- ft2_push_message(error);
- i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
- c, index);
- if (aa)
- i_render_done(&render);
- return 0;
- }
- slot = handle->face->glyph;
- gm = &slot->metrics;
-
- if (gm->width) {
- error = FT_Render_Glyph(slot, aa ? ft_render_mode_normal : ft_render_mode_mono);
- if (error) {
- ft2_push_message(error);
- i_push_errorf(0, "rendering glyph 0x%04X (character \\x%02X)");
- if (aa)
- i_render_done(&render);
- return 0;
- }
- if (slot->bitmap.pixel_mode == ft_pixel_mode_mono) {
- bmp = slot->bitmap.buffer;
- for (y = 0; y < slot->bitmap.rows; ++y) {
- int pos = 0;
- int bit = 0x80;
- for (x = 0; x < slot->bitmap.width; ++x) {
- if (bmp[pos] & bit)
- i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, cl);
-
- bit >>= 1;
- if (bit == 0) {
- bit = 0x80;
- ++pos;
- }
- }
- bmp += slot->bitmap.pitch;
- }
- }
- else {
- /* grey scale or something we can treat as greyscale */
- /* we create a map to convert from the bitmap values to 0-255 */
- if (last_mode != slot->bitmap.pixel_mode
- || last_grays != slot->bitmap.num_grays) {
- if (!make_bmp_map(&slot->bitmap, map))
- return 0;
- last_mode = slot->bitmap.pixel_mode;
- last_grays = slot->bitmap.num_grays;
- }
-
- bmp = slot->bitmap.buffer;
- for (y = 0; y < slot->bitmap.rows; ++y) {
- if (last_mode == ft_pixel_mode_grays &&
- last_grays != 255) {
- for (x = 0; x < slot->bitmap.width; ++x)
- bmp[x] = map[bmp[x]];
- }
- i_render_color(&render, tx + slot->bitmap_left, ty-slot->bitmap_top+y,
- slot->bitmap.width, bmp, cl);
- bmp += slot->bitmap.pitch;
- }
- }
- }
-
- tx += slot->advance.x / 64;
- ty -= slot->advance.y / 64;
- }
-
- if (aa)
- i_render_done(&render);
-
- return 1;
-}
-
-/*
-=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa)
-
-Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given
-I<cheight> and I<cwidth>.
-
-If align is 0, then the text is rendered with the top-left of the
-first character at (I<tx>, I<ty>). If align is non-zero then the text
-is rendered with (I<tx>, I<ty>) aligned with the base-line of the
-characters.
-
-If aa is non-zero then the text is anti-aliased.
-
-Returns non-zero on success.
-
-=cut
-*/
-
-int
-i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
- double cheight, double cwidth, char const *text, size_t len, int align,
- int aa, int vlayout, int utf8) {
- int bbox[8];
- i_img *work;
- i_color cl, cl2;
- int x, y;
-
- mm_log((1, "i_ft2_cp(handle %p, im %p, tx %d, ty %d, channel %d, cheight %f, cwidth %f, text %p, len %d, ...)\n",
- handle, im, tx, ty, channel, cheight, cwidth, text, len));
-
- if (vlayout && !FT_HAS_VERTICAL(handle->face)) {
- i_push_error(0, "face has no vertical metrics");
- return 0;
- }
-
- if (!i_ft2_bbox_r(handle, cheight, cwidth, text, len, vlayout, utf8, bbox))
- return 0;
-
- work = i_img_empty_ch(NULL, bbox[2]-bbox[0]+1, bbox[3]-bbox[1]+1, 1);
- cl.channel[0] = 255;
- if (!i_ft2_text(handle, work, -bbox[0], -bbox[1], &cl, cheight, cwidth,
- text, len, 1, aa, vlayout, utf8))
- return 0;
-
- if (!align) {
- tx -= bbox[4];
- ty += bbox[5];
- }
-
- /* render to the specified channel */
- /* this will be sped up ... */
- for (y = 0; y < work->ysize; ++y) {
- for (x = 0; x < work->xsize; ++x) {
- i_gpix(work, x, y, &cl);
- i_gpix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
- cl2.channel[channel] = cl.channel[0];
- i_ppix(im, tx + x + bbox[0], ty + y + bbox[1], &cl2);
- }
- }
- i_img_destroy(work);
- return 1;
-}
-
-/*
-=item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
-
-Check if the given characters are defined by the font.
-
-Returns the number of characters that were checked.
-
-=cut
-*/
-int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
- int utf8, char *out) {
- int count = 0;
- mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
- handle, text, 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;
- }
-
- index = FT_Get_Char_Index(handle->face, c);
- *out++ = index != 0;
- ++count;
- }
-
- return count;
-}
-
-/* uses a method described in fterrors.h to build an error translation
- function
-*/
-#undef __FTERRORS_H__
-#define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return;
-#define FT_ERROR_START_LIST
-#define FT_ERROR_END_LIST
-
-/*
-=back
-
-=head2 Internal Functions
-
-These functions are used in the implementation of freetyp2.c and should not
-(usually cannot) be called from outside it.
-
-=over
-
-=item ft2_push_message(int code)
-
-Pushes an error message corresponding to code onto the error stack.
-
-=cut
-*/
-static void ft2_push_message(int code) {
- char unknown[40];
-
- switch (code) {
-#include FT_ERRORS_H
- }
-
- sprintf(unknown, "Unknown Freetype2 error code 0x%04X\n", code);
- i_push_error(code, unknown);
-}
-
-/*
-=item make_bmp_map(FT_Bitmap *bitmap, unsigned char *map)
-
-Creates a map to convert grey levels from the glyphs bitmap into
-values scaled 0..255.
-
-=cut
-*/
-static int
-make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
- int scale;
- int i;
-
- switch (bitmap->pixel_mode) {
- case ft_pixel_mode_grays:
- scale = bitmap->num_grays;
- break;
-
- default:
- i_push_errorf(0, "I can't handle pixel mode %d", bitmap->pixel_mode);
- return 0;
- }
-
- /* build the table */
- for (i = 0; i < scale; ++i)
- map[i] = i * 255 / (bitmap->num_grays - 1);
-
- return 1;
-}
-
-/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false
- positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */
-#ifndef FREETYPE_PATCH
-#define FREETYPE_PATCH 4
-#endif
-
-/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */
-#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5)
-/* #define IM_HAS_FACE_NAME 0 */
-
-/*
-=item i_ft2_face_name(handle, name_buf, name_buf_size)
-
-Fills the given buffer with the Postscript Face name of the font,
-if there is one.
-
-=cut
-*/
-
-int
-i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
-#if IM_HAS_FACE_NAME
- char const *name = FT_Get_Postscript_Name(handle->face);
-
- i_clear_error();
-
- if (name) {
- 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 available");
- *name_buf = '\0';
-
- return 0;
- }
-#else
- i_clear_error();
- i_push_error(0, "Freetype 2.0.6 or later required");
- *name_buf = '\0';
-
- return 0;
-#endif
-}
-
-int
-i_ft2_can_face_name(void) {
- return IM_HAS_FACE_NAME;
-}
-
-/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */
-/* well, I assume FREETYPE_MAJOR is 2, since we're here */
-#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1)
-#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face))
-#endif
-
-int
-i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf,
- size_t name_buf_size, int reliable_only) {
-#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
- i_clear_error();
- *name_buf = '\0';
- i_push_error(0, "FT2 configured without glyph name support");
-
- return 0;
-#else
- FT_UInt index;
-
- i_clear_error();
-
- if (!FT_HAS_GLYPH_NAMES(handle->face)) {
- i_push_error(0, "no glyph names in font");
- *name_buf = '\0';
- return 0;
- }
- if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
- i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
- *name_buf = '\0';
- return 0;
- }
-
- index = FT_Get_Char_Index(handle->face, ch);
-
- if (index) {
- FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
- name_buf_size);
- if (error) {
- ft2_push_message(error);
- *name_buf = '\0';
- return 0;
- }
- if (*name_buf) {
- return strlen(name_buf) + 1;
- }
- else {
- return 0;
- }
- }
- else {
- i_push_error(0, "no glyph for that character");
- *name_buf = 0;
- return 0;
- }
-#endif
-}
-
-int
-i_ft2_can_do_glyph_names(void) {
-#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
- return 0;
-#else
- return 1;
-#endif
-}
-
-int
-i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
-#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
- return 0;
-#else
- return FT_Has_PS_Glyph_Names(handle->face);
-#endif
-}
-
-int
-i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
- i_clear_error();
-#ifdef IM_FT2_MM
- return handle->has_mm;
-#else
- return 0;
-#endif
-}
-
-int
-i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
-#ifdef IM_FT2_MM
- int i;
- FT_Multi_Master *mms = &handle->mm;
-
- i_clear_error();
- if (!handle->has_mm) {
- i_push_error(0, "Font has no multiple masters");
- return 0;
- }
- mm->num_axis = mms->num_axis;
- mm->num_designs = mms->num_designs;
- for (i = 0; i < mms->num_axis; ++i) {
- mm->axis[i].name = mms->axis[i].name;
- mm->axis[i].minimum = mms->axis[i].minimum;
- mm->axis[i].maximum = mms->axis[i].maximum;
- }
-
- return 1;
-#else
- i_clear_error();
- i_push_error(0, "Multiple master functions unavailable");
- return 0;
-#endif
-}
-
-int
-i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords) {
-#ifdef IM_FT2_MM
- int i;
- FT_Long ftcoords[T1_MAX_MM_AXIS];
- FT_Error error;
-
- i_clear_error();
- if (!handle->has_mm) {
- i_push_error(0, "Font has no multiple masters");
- return 0;
- }
- if (coord_count != handle->mm.num_axis) {
- i_push_error(0, "Number of MM coords doesn't match MM axis count");
- return 0;
- }
- for (i = 0; i < coord_count; ++i)
- ftcoords[i] = coords[i];
-
- error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
- if (error) {
- ft2_push_message(error);
- return 0;
- }
-
- return 1;
-#else
- i_clear_error();
- i_push_error(0, "Multiple master functions unavailable");
-
- return 0;
-#endif
-}
-
-/*
-=back
-
-=head1 AUTHOR
-
-Tony Cook <tony@develop-help.com>, with a fair amount of help from
-reading the code in font.c.
-
-=head1 SEE ALSO
-
-font.c, Imager::Font(3), Imager(3)
-
-http://www.freetype.org/
-
-=cut
-*/
-
#endif /* End of freetype headers */
-#ifdef HAVE_FT2
-
-extern int i_ft2_init(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);
-extern int i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi);
-extern int i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix);
-extern int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting);
-extern int i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
- char const *text, size_t len, int *bbox, int utf8);
-extern int i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
- char const *text, size_t len, int vlayout, int utf8, int *bbox);
-extern int i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
- const i_color *cl, double cheight, double cwidth,
- char const *text, size_t len, int align, int aa,
- int vlayout, int utf8);
-extern int i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
- int channel, double cheight, double cwidth,
- char const *text, size_t len, int align, int aa,
- int vlayout, int utf8);
-extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
- int utf8, char *work);
-extern int i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf,
- size_t name_buf_size);
-extern int i_ft2_can_face_name(void);
-extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch,
- char *name_buf, size_t name_buf_size,
- int reliable_only);
-extern int i_ft2_can_do_glyph_names(void);
-extern int i_ft2_face_has_glyph_names(FT2_Fonthandle *handle);
-
-extern int i_ft2_get_multiple_masters(FT2_Fonthandle *handle,
- i_font_mm *mm);
-extern int
-i_ft2_is_multiple_master(FT2_Fonthandle *handle);
-extern int
-i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords);
-#endif
-
/* functions for reading and writing formats */
/* general reader callback
#define im_assert(x) (void)(0)
#endif
+i_img_dim i_minx(i_img_dim a, i_img_dim b);
+i_img_dim i_maxx(i_img_dim x, i_img_dim y);
+
+#define i_min(a, b) i_minx((a), (b))
+#define i_max(a, b) i_maxx((a), (b))
+
#endif
#ifndef _DATATYPES_H_
#define _DATATYPES_H_
+#include <stddef.h>
#include "imconfig.h"
#include "imio.h"
=cut
*/
+
typedef int i_img_dim;
/*
struct i_fill_tag;
typedef void (*i_fill_with_color_f)
- (struct i_fill_tag *fill, int x, int y, int width, int channels,
+(struct i_fill_tag *fill, i_img_dim x, i_img_dim y, i_img_dim width, int channels,
i_color *data);
typedef void (*i_fill_with_fcolor_f)
- (struct i_fill_tag *fill, int x, int y, int width, int channels,
+ (struct i_fill_tag *fill, i_img_dim x, i_img_dim y, i_img_dim width, int channels,
i_fcolor *data);
typedef void (*i_fill_destroy_f)(struct i_fill_tag *fill);
*/
typedef void (*i_fill_combine_f)(i_color *out, i_color *in, int channels,
- int count);
+ i_img_dim count);
typedef void (*i_fill_combinef_f)(i_fcolor *out, i_fcolor *in, int channels,
- int count);
+ i_img_dim count);
/* fountain fill types */
typedef enum {
#endif
-#ifdef HAVE_FT2
-
-typedef struct FT2_Fonthandle FT2_Fonthandle;
-
-#endif
-
/* transparency handling for quantized output */
typedef enum i_transp_tag {
tr_none, /* ignore any alpha channel */
#include "iolayert.h"
-#include "rendert.h"
+typedef struct i_render_tag i_render;
#endif
#include "imexttypes.h"
#include "imager.h"
+#include "imio.h"
/*
DON'T ADD CASTS TO THESE
i_gsampf_bg,
i_get_file_background,
i_get_file_backgroundf,
- i_utf8_advance
+ i_utf8_advance,
+ i_render_new,
+ i_render_delete,
+ i_render_color,
+ i_render_fill,
+ i_render_line,
+ i_render_linef
};
/* in general these functions aren't called by Imager internally, but
#define i_utf8_advance(p, s) ((im_extt->f_i_utf8_advance)((p), (s)))
+#define i_render_new(im, width) ((im_extt->f_i_render_new)((im), (width)))
+#define i_render_delete(r) ((im_extt->f_i_render_delete)(r))
+#define i_render_color(r, x, y, width, src, color) \
+ ((im_extt->f_i_render_color)((r), (x), (y), (width), (src), (color)))
+#define i_render_fill(r, x, y, width, src, fill) \
+ ((im_extt->f_i_render_fill)((r), (x), (y), (width), (src), (fill)))
+#define i_render_line(r, x, y, width, src, line, combine) \
+ ((im_extt->f_i_render_line)((r), (x), (y), (width), (src), (line), (combine)))
+#define i_render_linef(r, x, y, width, src, line, combine) \
+ ((im_extt->f_i_render_linef)((r), (x), (y), (width), (src), (line), (combine)))
+
#endif
void (*f_i_get_file_background)(i_img *im, i_color *bg);
void (*f_i_get_file_backgroundf)(i_img *im, i_fcolor *bg);
unsigned long (*f_i_utf8_advance)(char const **p, size_t *len);
+ i_render *(*f_i_render_new)(i_img *im, i_img_dim width);
+ void (*f_i_render_delete)(i_render *r);
+ void (*f_i_render_color)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_color const *color);
+ void (*f_i_render_fill)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_fill_t *fill);
+ void (*f_i_render_line)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, const i_sample_t *src,
+ i_color *line, i_fill_combine_f combine);
+ void (*f_i_render_linef)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, const double *src,
+ i_fcolor *line, i_fill_combinef_f combine);
/* IMAGER_API_LEVEL 6 functions will be added here */
} im_ext_funcs;
void *i_mempool_alloc(i_mempool *mp, size_t size);
void i_mempool_destroy(i_mempool *mp);
-
-
#ifdef _MSC_VER
#undef min
#undef max
extern unsigned long i_utf8_advance(char const **p, size_t *len);
-/* XXX Shouldn't these go away? */
-
-int i_min(int a,int b);
-int i_max(int x,int y);
-
#endif
typedef TT_Fonthandle* Imager__Font__TT;
#endif
-#ifdef HAVE_FT2
-typedef FT2_Fonthandle* Imager__Font__FT2;
-#endif
-
/* for the fill objects
Since a fill object may later have dependent images, (or fills!)
we need perl wrappers - oh well
#include "rendert.h"
extern void
-i_render_init(i_render *r, i_img *im, int width);
+i_render_init(i_render *r, i_img *im, i_img_dim width);
extern void
i_render_done(i_render *r);
extern void
-i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
- i_color const *color);
+i_render_color(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_color const *color);
extern void
-i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
- i_fill_t *fill);
+i_render_fill(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_fill_t *fill);
extern void
-i_render_line(i_render *r, int x, int y, int width, const i_sample_t *src,
- i_color *line, i_fill_combine_f combine);
+i_render_line(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ const i_sample_t *src, i_color *line, i_fill_combine_f combine);
extern void
-i_render_linef(i_render *r, int x, int y, int width, const double *src,
- i_fcolor *line, i_fill_combinef_f combine);
+i_render_linef(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ const double *src, i_fcolor *line, i_fill_combinef_f combine);
+
+extern i_render *
+i_render_new(i_img *im, i_img_dim width);
+extern void
+i_render_delete(i_render *r);
#endif
#include "imager.h"
+#include "imageri.h"
#include <stdlib.h>
#ifndef _MSC_VER
#include <unistd.h>
#undef min
#undef max
-int
-i_min(int a,int b) {
+i_img_dim
+i_minx(i_img_dim a, i_img_dim b) {
if (a<b) return a; else return b;
}
-int
-i_max(int a,int b) {
+i_img_dim
+i_maxx(i_img_dim a, i_img_dim b) {
if (a>b) return a; else return b;
}
};
/*
-=item utf8_advance(char **p, int *len)
+=item i_utf8_advance(char **p, size_t *len)
-Retreive a UTF8 character from the stream.
+Retrieve a C<UTF-8> character from the stream.
Modifies *p and *len to indicate the consumed characters.
-This doesn't support the extended UTF8 encoding used by later versions
-of Perl.
+This doesn't support the extended C<UTF-8> encoding used by later
+versions of Perl.
-This doesn't check that the UTF8 charecter is using the shortest
+This doesn't check that the C<UTF-8> character is using the shortest
possible representation.
=cut
#endif
#include <string.h>
#include <errno.h>
+#include "imageri.h"
#define IOL_DEB(x)
color.rgba.r = 255; color.rgba.g = 0; color.rgba.b = 255;
+ # Blit tools
+
# Data Types
i_img *img;
i_color black;
=head1 DESCRIPTION
+=head2 Blit tools
+
+=over
+
+=item i_render_color(r, x, y, width, source, color)
+
+Render the given color with the coverage specified by C<source[0]> to
+C<source[width-1]>.
+
+Renders in normal combine mode.
+
+
+=for comment
+From: File render.im
+
+=item i_render_delete(r)
+
+Release an C<i_render> object.
+
+
+=for comment
+From: File render.im
+
+=item i_render_fill(r, x, y, width, source, fill)
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+
+=for comment
+From: File render.im
+
+=item i_render_line(r, x, y, width, source, fill)
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+
+=for comment
+From: File render.im
+
+=item i_render_linef(r, x, y, width, source, fill)
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+
+=for comment
+From: File render.im
+
+=item i_render_new(im, width)
+
+Allocate a new C<i_render> object and initialize it.
+
+
+=for comment
+From: File render.im
+
+
+=back
+
=head2 Data Types
=over
=for comment
From: File imext.c
+=item i_gsamp_bg(im, l, r, y, samples, out_channels, background)
+
+
+Like C<i_gsampf()> but applies the source image color over a supplied
+background color.
+
+This is intended for output to image formats that don't support alpha
+channels.
+
+
+=for comment
+From: File paste.im
+
=item i_gsampf(im, left, right, y, samples, channels, channel_count)
=back
+=head2 Uncategorized functions
-=head1 UNDOCUMENTED
+=over
-The following API functions are undocumented so far, hopefully this
-will change:
+=item i_utf8_advance(char **p, size_t *len)
-=over
+Retrieve a C<UTF-8> character from the stream.
-=item *
+Modifies *p and *len to indicate the consumed characters.
-B<i_gsamp_bg>
+This doesn't support the extended C<UTF-8> encoding used by later
+versions of Perl.
+
+This doesn't check that the C<UTF-8> character is using the shortest
+possible representation.
-=item *
-B<i_utf8_advance>
+=for comment
+From: File io.c
description => 'T1Lib',
},
ft2=>{
- class=>'Imager::Font::FreeType2',
- module=>'Imager/Font/FreeType2.pm',
+ class=>'Imager::Font::FT2',
+ module=>'Imager/Font/FT2.pm',
files=>'.*\.(pfa|pfb|otf|ttf|fon|fnt|dfont|pcf(\.gz)?)$',
description => 'FreeType 2.x',
},
# this currently should only contain file based types, don't add w32
my @priority = qw(t1 tt ft2 ifs);
-# when Imager::Font is loaded, Imager.xs has not been bootstrapped yet
-# this function is called from Imager.pm to finish initialization
-sub __init {
- @priority = grep Imager::i_has_format($_), @priority;
- for my $driver_name (grep Imager::i_has_format($_), keys %drivers) {
- $drivers{$driver_name}{enabled} = 1;
- }
-}
-
-# search method
-# 1. start by checking if file is the parameter
-# 1a. if so qualify path and compare to the cache.
-# 2a. if in cache - take it's id from there and increment count.
-#
-
sub new {
my $class = shift;
my $self = {};
$file = $hsh{'file'};
$type = $hsh{'type'};
- if (!defined($type) or !$drivers{$type} or !$drivers{$type}{enabled}) {
+ if (defined $type) {
+ unless ($drivers{$type}) {
+ Imager->_set_error("Unknown font type $type");
+ return;
+ }
+
+ unless ($Imager::formats{$type}) {
+ Imager->_set_error("The $type {$drivers{$type}) font driver is not installed");
+ return;
+ }
+ }
+ else {
for my $drv (@priority) {
undef $type;
my $re = $drivers{$drv}{files} or next;
package Imager::Font::FreeType2;
use strict;
-use Imager::Color;
+use Imager::Font::FT2;
use vars qw(@ISA $VERSION);
-@ISA = qw(Imager::Font);
-
-$VERSION = "1.014";
-
-*_first = \&Imager::Font::_first;
-
-sub new {
- my $class = shift;
- my %hsh=(color=>Imager::Color->new(255,0,0,0),
- size=>15,
- @_);
-
- unless ($hsh{file}) {
- $Imager::ERRSTR = "No font file specified";
- return;
- }
- unless (-e $hsh{file}) {
- $Imager::ERRSTR = "Font file $hsh{file} not found";
- return;
- }
- unless ($Imager::formats{ft2}) {
- $Imager::ERRSTR = "Freetype2 not supported in this build";
- return;
- }
- my $id = i_ft2_new($hsh{file}, $hsh{'index'} || 0);
- unless ($id) { # the low-level code may miss some error handling
- $Imager::ERRSTR = Imager::_error_as_msg();
- return;
- }
- return bless {
- id => $id,
- aa => $hsh{aa} || 0,
- file => $hsh{file},
- type => 't1',
- size => $hsh{size},
- color => $hsh{color},
- utf8 => $hsh{utf8},
- vlayout => $hsh{vlayout},
- }, $class;
-}
-
-sub _draw {
- my $self = shift;
- my %input = @_;
- if (exists $input{channel}) {
- i_ft2_cp($self->{id}, $input{image}{IMG}, $input{'x'}, $input{'y'},
- $input{channel}, $input{size}, $input{sizew} || 0,
- $input{string}, , $input{align}, $input{aa}, $input{vlayout},
- $input{utf8});
- } else {
- i_ft2_text($self->{id}, $input{image}{IMG},
- $input{'x'}, $input{'y'},
- $input{color}, $input{size}, $input{sizew} || 0,
- $input{string}, $input{align}, $input{aa}, $input{vlayout},
- $input{utf8});
- }
-}
-
-sub _bounding_box {
- my $self = shift;
- my %input = @_;
-
- return i_ft2_bbox($self->{id}, $input{size}, $input{sizew}, $input{string},
- $input{utf8});
-}
-
-sub dpi {
- my $self = shift;
- my @old = i_ft2_getdpi($self->{id});
- if (@_) {
- my %hsh = @_;
- my $result;
- unless ($hsh{xdpi} && $hsh{ydpi}) {
- if ($hsh{dpi}) {
- $hsh{xdpi} = $hsh{ydpi} = $hsh{dpi};
- }
- else {
- $Imager::ERRSTR = "dpi method requires xdpi and ydpi or just dpi";
- return;
- }
- i_ft2_setdpi($self->{id}, $hsh{xdpi}, $hsh{ydpi}) or return;
- }
- }
-
- return @old;
-}
-
-sub hinting {
- my ($self, %opts) = @_;
-
- i_ft2_sethinting($self->{id}, $opts{hinting} || 0);
-}
-
-sub _transform {
- my $self = shift;
-
- my %hsh = @_;
- my $matrix = $hsh{matrix} or return undef;
-
- return i_ft2_settransform($self->{id}, $matrix)
-}
-
-sub utf8 {
- return 1;
-}
-
-# check if the font has the characters in the given string
-sub has_chars {
- my ($self, %hsh) = @_;
-
- unless (defined $hsh{string} && length $hsh{string}) {
- $Imager::ERRSTR = "No string supplied to \$font->has_chars()";
- return;
- }
- return i_ft2_has_chars($self->{id}, $hsh{string},
- _first($hsh{'utf8'}, $self->{utf8}, 0));
-}
-
-sub face_name {
- my ($self) = @_;
-
- i_ft2_face_name($self->{id});
-}
-
-sub can_glyph_names {
- i_ft2_can_do_glyph_names();
-}
-
-sub glyph_names {
- my ($self, %input) = @_;
-
- my $string = $input{string};
- defined $string
- or return Imager->_set_error("no string parameter passed to glyph_names");
- my $utf8 = _first($input{utf8}, 0);
- my $reliable_only = _first($input{reliable_only}, 1);
-
- my @names = i_ft2_glyph_name($self->{id}, $string, $utf8, $reliable_only);
- @names or return Imager->_set_error(Imager->_error_as_msg);
-
- return @names if wantarray;
- return pop @names;
-}
-
-sub is_mm {
- my ($self) = @_;
-
- i_ft2_is_multiple_master($self->{id});
-}
-
-sub mm_axes {
- my ($self) = @_;
-
- my ($num_axis, $num_design, @axes) =
- i_ft2_get_multiple_masters($self->{id})
- or return Imager->_set_error(Imager->_error_as_msg);
-
- return @axes;
-}
-
-sub set_mm_coords {
- my ($self, %opts) = @_;
-
- $opts{coords}
- or return Imager->_set_error("Missing coords parameter");
- ref($opts{coords}) && $opts{coords} =~ /ARRAY\(0x[\da-f]+\)$/
- or return Imager->_set_error("coords parameter must be an ARRAY ref");
-
- i_ft2_set_mm_coords($self->{id}, @{$opts{coords}})
- or return Imager->_set_error(Imager->_error_as_msg);
-
- return 1;
-}
+@ISA = qw(Imager::Font::FT2);
+
+$VERSION = "1.020";
+
1;
This is intended for output to image formats that don't support alpha
channels.
+=cut
+
=item i_gsampf_bg(im, l, r, y, samples, out_channels, background)
=category Drawing
#include "draw.h"
#include "log.h"
#include "imrender.h"
+#include "imageri.h"
#define IMTRUNC(x) ((int)((x)*16))
8-bit (or bigger indexed) png files at some point
*/
#include "imager.h"
+#include "imageri.h"
static void makemap_addi(i_quantize *, i_img **imgs, int count);
static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
#include "regmach.h"
#include <float.h>
+#include "imageri.h"
/*#define DEBUG*/
#ifdef DEBUG
#define RENDER_MAGIC 0x765AE
-typedef void (*render_color_f)(i_render *, int, int, int, unsigned char const *src, i_color const *color);
+typedef void (*render_color_f)(i_render *, i_img_dim, i_img_dim, i_img_dim, unsigned char const *src, i_color const *color);
#define i_has_alpha(channels) ((channels) == 2 || (channels) == 4)
#code
-static void IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
-static void IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width, unsigned char const *src, i_color const *color);
+static void IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
+static void IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width, unsigned char const *src, i_color const *color);
static render_color_f IM_SUFFIX(render_color_tab)[] =
{
IM_SUFFIX(render_color_alpha),
};
-static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
-static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+static void IM_SUFFIX(combine_line_noalpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+static void IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
/* the copy variant copies the source alpha to the the output alpha channel */
-static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+static void IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
-static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
-static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count);
+static void IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
+static void IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count);
#/code
+/*
+=item i_render_new(im, width)
+=category Blit tools
+
+Allocate a new C<i_render> object and initialize it.
+
+=cut
+*/
+
+i_render *
+i_render_new(i_img *im, i_img_dim width) {
+ i_render *r = mymalloc(sizeof(i_render));
+
+ i_render_init(r, im, width);
+
+ return r;
+}
+
+/*
+=item i_render_delete(r)
+=category Blit tools
+
+Release an C<i_render> object.
+
+=cut
+*/
+
void
-i_render_init(i_render *r, i_img *im, int width) {
+i_render_delete(i_render *r) {
+ i_render_done(r);
+ myfree(r);
+}
+
+void
+i_render_init(i_render *r, i_img *im, i_img_dim width) {
r->magic = RENDER_MAGIC;
r->im = im;
r->line_width = width;
}
static void
-alloc_line(i_render *r, int width, int eight_bit) {
+alloc_line(i_render *r, i_img_dim width, i_img_dim eight_bit) {
if (width > r->line_width) {
- int new_width = r->line_width * 2;
+ i_img_dim new_width = r->line_width * 2;
if (new_width < width)
new_width = width;
}
static void
-alloc_fill_line(i_render *r, int width, int eight_bit) {
+alloc_fill_line(i_render *r, i_img_dim width, int eight_bit) {
if (width > r->fill_width) {
- int new_width = r->fill_width * 2;
+ i_img_dim new_width = r->fill_width * 2;
if (new_width < width)
new_width = width;
}
}
+/*
+=item i_render_color(r, x, y, width, source, color)
+=category Blit tools
+
+Render the given color with the coverage specified by C<source[0]> to
+C<source[width-1]>.
+
+Renders in normal combine mode.
+
+=cut
+*/
+
void
-i_render_color(i_render *r, int x, int y, int width, unsigned char const *src,
- i_color const *color) {
+i_render_color(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_color const *color) {
i_img *im = r->im;
if (y < 0 || y >= im->ysize)
return;
#/code
}
+/*
+=item i_render_fill(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+*/
+
void
-i_render_fill(i_render *r, int x, int y, int width, unsigned char const *src,
- i_fill_t *fill) {
+i_render_fill(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ unsigned char const *src, i_fill_t *fill) {
i_img *im = r->im;
int fill_channels = im->channels;
if (src) {
unsigned char const *srcc = src;
IM_COLOR *fillc = r->IM_SUFFIX(fill_line);
- int work_width = width;
+ i_img_dim work_width = width;
while (work_width) {
if (*srcc == 0) {
fillc->channel[fill_channels-1] = 0;
}
else {
if (src) {
- int work_width = width;
+ i_img_dim work_width = width;
IM_COLOR *srcc = r->IM_SUFFIX(fill_line);
IM_COLOR *destc = r->IM_SUFFIX(line);
int ch;
}
static void
-dump_src(const char *note, unsigned char const *src, int width) {
- int i;
+dump_src(const char *note, unsigned char const *src, i_img_dim width) {
+ i_img_dim i;
printf("%s - %p/%d\n", note, src, width);
for (i = 0; i < width; ++i) {
printf("%02x ", src[i]);
#code
+/*
+=item i_render_line(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+
+=item i_render_linef(r, x, y, width, source, fill)
+=category Blit tools
+
+Render the given fill with the coverage in C<source[0]> through
+C<source[width-1]>.
+
+=cut
+*/
+
void
-IM_RENDER_LINE(i_render *r, int x, int y, int width, const IM_SAMPLE_T *src,
- IM_COLOR *line, IM_FILL_COMBINE_F combine) {
+IM_RENDER_LINE(i_render *r, i_img_dim x, i_img_dim y, i_img_dim width,
+ const IM_SAMPLE_T *src, IM_COLOR *line,
+ IM_FILL_COMBINE_F combine) {
i_img *im = r->im;
int src_chans = im->channels;
if (combine) {
if (src) {
- int work_width = width;
+ i_img_dim work_width = width;
IM_COLOR *linep = line;
const IM_SAMPLE_T *srcp = src;
int alpha_chan = src_chans - 1;
}
else {
if (src) {
- int work_width = width;
+ i_img_dim work_width = width;
IM_COLOR *srcc = line;
IM_COLOR *destc = r->IM_SUFFIX(line);
static
void
-IM_SUFFIX(render_color_13)(i_render *r, int x, int y, int width,
- unsigned char const *src, i_color const *color) {
+IM_SUFFIX(render_color_13)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_color const *color) {
i_img *im = r->im;
IM_COLOR *linep = r->IM_SUFFIX(line);
int ch, channels = im->channels;
- int fetch_offset;
+ i_img_dim fetch_offset;
#undef STORE_COLOR
#ifdef IM_EIGHT_BIT
#define STORE_COLOR (*color)
static
void
-IM_SUFFIX(render_color_alpha)(i_render *r, int x, int y, int width,
- unsigned char const *src, i_color const *color) {
+IM_SUFFIX(render_color_alpha)(i_render *r, i_img_dim x, i_img_dim y,
+ i_img_dim width, unsigned char const *src,
+ i_color const *color) {
IM_COLOR *linep = r->IM_SUFFIX(line);
int ch;
int alpha_channel = r->im->channels - 1;
- int fetch_offset;
+ i_img_dim fetch_offset;
#undef STORE_COLOR
#ifdef IM_EIGHT_BIT
#define STORE_COLOR (*color)
static void
IM_SUFFIX(combine_line_alpha)(IM_COLOR *out, IM_COLOR const *in,
- int channels, int count) {
+ int channels, i_img_dim count) {
int ch;
int alpha_channel = channels - 1;
static void
IM_SUFFIX(combine_line_noalpha)
- (IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
+ (IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
int ch;
while (count) {
static void
IM_SUFFIX(combine_line_alpha_na)(IM_COLOR *out, IM_COLOR const *in,
- int channels, int count) {
+ int channels, i_img_dim count) {
int ch;
int alpha_channel = channels - 1;
}
static void
-IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
+IM_SUFFIX(combine_line)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
if (channels == 2 || channels == 4)
IM_SUFFIX(combine_line_alpha)(out, in, channels, count);
else
}
static void
-IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, int count) {
+IM_SUFFIX(combine_line_na)(IM_COLOR *out, IM_COLOR const *in, int channels, i_img_dim count) {
if (channels == 2 || channels == 4)
IM_SUFFIX(combine_line_alpha_na)(out, in, channels, count);
else
IM_SUFFIX(combine_line_noalpha)(out, in, channels, count);
}
-static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, int);
-static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, int);
+static void IM_SUFFIX(combine_alphablend)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_mult)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_dissolve)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_add)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_subtract)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_diff)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_darken)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_lighten)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_hue)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_sat)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_value)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
+static void IM_SUFFIX(combine_color)(IM_COLOR *, IM_COLOR *, int, i_img_dim);
static const IM_FILL_COMBINE_F IM_SUFFIX(combines)[] =
{
static void
-IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_alphablend)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
IM_SUFFIX(combine_line)(out, in, channels, count);
}
Dc' = Sc.Sa.Dc + Dc.(1 - Sa)
*/
static void
-IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_mult)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int ch;
IM_COLOR *inp = in;
IM_COLOR *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
int color_channels = i_color_channels(channels);
if (i_has_alpha(channels)) {
}
static void
-IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_dissolve)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int color_channels = i_color_channels(channels);
int ch;
*/
static void
-IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_add)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int ch;
int color_channels = i_color_channels(channels);
- int work_count = count;
+ i_img_dim work_count = count;
IM_COLOR *inp = in;
IM_COLOR *outp = out;
channel to apply that to the target.
*/
static void
-IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_subtract)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int ch;
IM_COLOR const *inp = in;
IM_COLOR *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
int color_channels = i_color_channels(channels);
if (i_has_alpha(channels)) {
Da' = Sa + Da - Sa.Da
*/
static void
-IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_diff)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int ch;
IM_COLOR const *inp = in;
IM_COLOR *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
int color_channels = i_color_channels(channels);
if (i_has_alpha(channels)) {
*/
static void
IM_SUFFIX(combine_darken)(IM_COLOR *out, IM_COLOR *in, int channels,
- int count) {
+ i_img_dim count) {
int ch;
IM_COLOR const *inp = in;
IM_COLOR *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
int color_channels = i_color_channels(channels);
if (i_has_alpha(channels)) {
}
static void
-IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_lighten)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
int ch;
IM_COLOR const *inp = in;
IM_COLOR *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
int color_channels = i_color_channels(channels);
if (i_has_alpha(channels)) {
#endif
static void
-IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_hue)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
if (channels > 2) {
IM_COLOR *inp = in;
IM_COLOR const *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
if (i_has_alpha(channels)) {
while (work_count--) {
}
static void
-IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_sat)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
if (channels > 2) {
IM_COLOR *inp = in;
IM_COLOR const *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
while (work_count--) {
IM_COLOR c = *inp;
}
static void
-IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_value)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
if (channels > 2) {
IM_COLOR *inp = in;
IM_COLOR const *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
while (work_count--) {
IM_COLOR c = *inp;
}
static void
-IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, int count) {
+IM_SUFFIX(combine_color)(IM_COLOR *out, IM_COLOR *in, int channels, i_img_dim count) {
if (channels > 2) {
IM_COLOR *inp = in;
IM_COLOR const *outp = out;
- int work_count = count;
+ i_img_dim work_count = count;
while (work_count--) {
IM_COLOR c = *inp;
#ifndef IMAGER_RENDERT_H
#define IMAGER_RENDERT_H
-typedef struct {
+#include "imdatatypes.h"
+
+struct i_render_tag {
int magic;
i_img *im;
- int line_width;
+ i_img_dim line_width;
i_color *line_8;
i_fcolor *line_double;
- int fill_width;
+ i_img_dim fill_width;
i_color *fill_line_8;
i_fcolor *fill_line_double;
-} i_render;
+};
#endif
+++ /dev/null
-#!perl -w
-use strict;
-use Test::More tests => 189;
-use Cwd qw(getcwd abs_path);
-++$|;
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl test.pl'
-
-######################### We start with some black magic to print on failure.
-
-# Change 1..1 below to 1..last_test_to_print .
-# (It may become useful if the test is moved to ./t subdirectory.)
-
-BEGIN { use_ok(Imager => ':all') }
-
-use Imager::Test qw(diff_text_with_nul is_color3);
-
-init_log("testout/t38ft2font.log",2);
-
-my $deffont = "fontfiles/dodge.ttf";
-
-my @base_color = (64, 255, 64);
-
-SKIP:
-{
- i_has_format("ft2") or skip("no freetype2 library found", 188);
-
- print "# has ft2\n";
-
- my $fontname=$ENV{'TTFONTTEST'} || $deffont;
-
- -f $fontname or skip("cannot find fontfile $fontname", 188);
-
-
- my $bgcolor=i_color_new(255,0,0,0);
- my $overlay=Imager::ImgRaw::new(200,70,3);
-
- my $ttraw=Imager::Font::FreeType2::i_ft2_new($fontname, 0);
-
- $ttraw or print Imager::_error_as_msg(),"\n";
- ok($ttraw, "loaded raw font");
-
- my @bbox=Imager::Font::FreeType2::i_ft2_bbox($ttraw, 50.0, 0, 'XMCLH', 0);
- print "#bbox @bbox\n";
-
- is(@bbox, 8, "i_ft2_bbox() returns 8 values");
-
- ok(Imager::Font::FreeType2::i_ft2_cp($ttraw,$overlay,5,50,1,50.0,50, 'XMCLH',1,1, 0, 0), "drawn to channel");
- i_line($overlay,0,50,100,50,$bgcolor,1);
-
- open(FH,">testout/t38ft2font.ppm") || die "cannot open testout/t38ft2font.ppm\n";
- binmode(FH);
- my $IO = Imager::io_new_fd(fileno(FH));
- ok(i_writeppm_wiol($overlay, $IO), "saved image");
- close(FH);
-
- $bgcolor=i_color_set($bgcolor,200,200,200,0);
- my $backgr=Imager::ImgRaw::new(500,300,3);
-
- # i_tt_set_aa(2);
- ok(Imager::Font::FreeType2::i_ft2_text($ttraw,$backgr,100,150,NC(255, 64, 64),200.0,50, 'MAW',1,1,0, 0), "drew MAW");
- Imager::Font::FreeType2::i_ft2_settransform($ttraw, [0.9659, 0.2588, 0, -0.2588, 0.9659, 0 ]);
- ok(Imager::Font::FreeType2::i_ft2_text($ttraw,$backgr,100,150,NC(0, 128, 0),200.0,50, 'MAW',0,1, 0, 0), "drew rotated MAW");
- i_line($backgr, 0,150, 499, 150, NC(0, 0, 255),1);
-
- open(FH,">testout/t38ft2font2.ppm") || die "cannot open testout/t38ft2font.ppm\n";
- binmode(FH);
- $IO = Imager::io_new_fd(fileno(FH));
- ok(i_writeppm_wiol($backgr,$IO), "saved second image");
- close(FH);
-
- my $oof = Imager::Font->new(file=>$fontname, type=>'ft2', 'index'=>0);
-
- ok($oof, "loaded OO font");
-
- my $im = Imager->new(xsize=>400, ysize=>250);
-
- ok($im->string(font=>$oof,
- text=>"Via OO",
- 'x'=>20,
- 'y'=>20,
- size=>60,
- color=>NC(255, 128, 255),
- aa => 1,
- align=>0), "drawn through OO interface");
- ok($oof->transform(matrix=>[1, 0.1, 0, 0, 1, 0]),
- "set matrix via OO interface");
- ok($im->string(font=>$oof,
- text=>"Shear",
- 'x'=>20,
- 'y'=>40,
- size=>60,
- sizew=>50,
- channel=>1,
- aa=>1,
- align=>1), "drawn transformed through OO");
- use Imager::Matrix2d ':handy';
- ok($oof->transform(matrix=>m2d_rotate(degrees=>-30)),
- "set transform from m2d module");
- ok($im->string(font=>$oof,
- text=>"SPIN",
- 'x'=>20,
- 'y'=>50,
- size=>50,
- sizew=>40,
- color=>NC(255,255,0),
- aa => 1,
- align=>0, vlayout=>0), "drawn first rotated");
-
- ok($im->string(font=>$oof,
- text=>"SPIN",
- 'x'=>20,
- 'y'=>50,
- size=>50,
- sizew=>40,
- channel=>2,
- aa => 1,
- align=>0, vlayout=>0), "drawn second rotated");
-
- $oof->transform(matrix=>m2d_identity());
- $oof->hinting(hinting=>1);
-
- # UTF8 testing
- # the test font (dodge.ttf) only supports one character above 0xFF that
- # I can see, 0x2010 HYPHEN (which renders the same as 0x002D HYPHEN MINUS)
- # an attempt at utf8 support
- # first attempt to use native perl UTF8
- SKIP:
- {
- skip("no native UTF8 support in this version of perl", 1)
- unless $] >= 5.006;
- my $text;
- # we need to do this in eval to prevent compile time errors in older
- # versions
- eval q{$text = "A\x{2010}A"}; # A, HYPHEN, A in our test font
- #$text = "A".chr(0x2010)."A"; # this one works too
- unless (ok($im->string(font=>$oof,
- text=>$text,
- 'x'=>20,
- 'y'=>200,
- size=>50,
- color=>NC(0,255,0),
- aa=>1), "drawn UTF natively")) {
- print "# ",$im->errstr,"\n";
- }
- }
-
- # an attempt using emulation of UTF8
- my $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
- #my $text = "A\xE2\x80\x90\x41\x{2010}";
- #substr($text, -1, 0) = '';
- unless (ok($im->string(font=>$oof,
- text=>$text,
- 'x'=>20,
- 'y'=>230,
- size=>50,
- color=>NC(255,128,0),
- aa=>1,
- utf8=>1), "drawn UTF emulated")) {
- print "# ",$im->errstr,"\n";
- }
-
- # just a bit of fun
- # well it was - it demostrates what happens when you combine
- # transformations and font hinting
- for my $steps (0..39) {
- $oof->transform(matrix=>m2d_rotate(degrees=>-$steps+5));
- # demonstrates why we disable hinting on a doing a transform
- # if the following line is enabled then the 0 degrees output sticks
- # out a bit
- # $oof->hinting(hinting=>1);
- $im->string(font=>$oof,
- text=>"SPIN",
- 'x'=>160,
- 'y'=>70,
- size=>65,
- color=>NC(255, $steps * 5, 200-$steps * 5),
- aa => 1,
- align=>0, );
- }
-
- $im->write(file=>'testout/t38_oo.ppm')
- or print "# could not save OO output: ",$im->errstr,"\n";
-
- my (@got) = $oof->has_chars(string=>"\x01H");
- ok(@got == 2, "has_chars returned 2 items");
- ok(!$got[0], "have no chr(1)");
- ok($got[1], "have 'H'");
- is($oof->has_chars(string=>"H\x01"), "\x01\x00",
- "scalar has_chars()");
-
- print "# OO bounding boxes\n";
- @bbox = $oof->bounding_box(string=>"hello", size=>30);
- my $bbox = $oof->bounding_box(string=>"hello", size=>30);
-
- is(@bbox, 8, "list bbox returned 8 items");
- ok($bbox->isa('Imager::Font::BBox'), "scalar bbox returned right class");
- ok($bbox->start_offset == $bbox[0], "start_offset");
- ok($bbox->end_offset == $bbox[2], "end_offset");
- ok($bbox->global_ascent == $bbox[3], "global_ascent");
- ok($bbox->global_descent == $bbox[1], "global_descent");
- ok($bbox->ascent == $bbox[5], "ascent");
- ok($bbox->descent == $bbox[4], "descent");
- ok($bbox->advance_width == $bbox[6], "advance_width");
-
- print "# aligned text output\n";
- my $alimg = Imager->new(xsize=>300, ysize=>300);
- $alimg->box(color=>'40FF40', filled=>1);
-
- $oof->transform(matrix=>m2d_identity());
- $oof->hinting(hinting=>1);
-
- align_test('left', 'top', 10, 10, $oof, $alimg);
- align_test('start', 'top', 10, 40, $oof, $alimg);
- align_test('center', 'top', 150, 70, $oof, $alimg);
- align_test('end', 'top', 290, 100, $oof, $alimg);
- align_test('right', 'top', 290, 130, $oof, $alimg);
-
- align_test('center', 'top', 150, 160, $oof, $alimg);
- align_test('center', 'center', 150, 190, $oof, $alimg);
- align_test('center', 'bottom', 150, 220, $oof, $alimg);
- align_test('center', 'baseline', 150, 250, $oof, $alimg);
-
- ok($alimg->write(file=>'testout/t38aligned.ppm'),
- "saving aligned output image");
-
- my $exfont = Imager::Font->new(file=>'fontfiles/ExistenceTest.ttf',
- type=>'ft2');
- SKIP:
- {
- ok($exfont, "loaded existence font") or
- skip("couldn't load test font", 11);
-
- # the test font is known to have a shorter advance width for that char
- my @bbox = $exfont->bounding_box(string=>"/", size=>100);
- is(@bbox, 8, "should be 8 entries");
- isnt($bbox[6], $bbox[2], "different advance width");
- my $bbox = $exfont->bounding_box(string=>"/", size=>100);
- ok($bbox->pos_width != $bbox->advance_width, "OO check");
-
- cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
-
- cmp_ok($bbox->display_width, '>', $bbox->advance_width,
- "check display width (roughly)");
-
- # check with a char that fits inside the box
- $bbox = $exfont->bounding_box(string=>"!", size=>100);
- print "# pos width ", $bbox->pos_width, "\n";
- is($bbox->pos_width, $bbox->advance_width,
- "check backwards compatibility");
- cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
- cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
- cmp_ok($bbox->display_width, '<', $bbox->advance_width,
- "display smaller than advance");
-
- # name tests
- # make sure the number of tests on each branch match
- if (Imager::Font::FreeType2::i_ft2_can_face_name()) {
- my $facename = Imager::Font::FreeType2::i_ft2_face_name($exfont->{id});
- print "# face name '$facename'\n";
- is($facename, 'ExistenceTest', "test face name");
- $facename = $exfont->face_name;
- is($facename, 'ExistenceTest', "test face name OO");
- }
- else {
- # make sure we get the error we expect
- my $facename = Imager::Font::FreeType2::i_ft2_face_name($exfont->{id});
- my ($msg) = Imager::_error_as_msg();
- ok(!defined($facename), "test face name not supported");
- print "# $msg\n";
- ok(scalar($msg =~ /or later required/), "test face name not supported");
- }
- }
-
- SKIP:
- {
- Imager::Font::FreeType2->can_glyph_names
- or skip("FT2 compiled without glyph names support", 9);
-
- # FT2 considers POST tables in TTF fonts unreliable, so use
- # a type 1 font, see below for TTF test
- my $exfont = Imager::Font->new(file=>'fontfiles/ExistenceTest.pfb',
- type=>'ft2');
- SKIP:
- {
- ok($exfont, "load Type 1 via FT2")
- or skip("couldn't load type 1 with FT2", 8);
- my @glyph_names =
- Imager::Font::FreeType2::i_ft2_glyph_name($exfont->{id}, "!J/");
- #use Data::Dumper;
- #print Dumper \@glyph_names;
- is($glyph_names[0], 'exclam', "check exclam name");
- ok(!defined($glyph_names[1]), "check for no J name");
- is($glyph_names[2], 'slash', "check slash name");
-
- # oo interfaces
- @glyph_names = $exfont->glyph_names(string=>"!J/");
- is($glyph_names[0], 'exclam', "check exclam name OO");
- ok(!defined($glyph_names[1]), "check for no J name OO");
- is($glyph_names[2], 'slash', "check slash name OO");
-
- # make sure a missing string parameter is handled correctly
- eval {
- $exfont->glyph_names();
- };
- is($@, "", "correct error handling");
- cmp_ok(Imager->errstr, '=~', qr/no string parameter/, "error message");
- }
-
- # freetype 2 considers truetype glyph name tables unreliable
- # due to some specific fonts, supplying reliable_only=>0 bypasses
- # that check and lets us get the glyph names even for truetype fonts
- # so we can test this stuff <sigh>
- # we can't use ExistenceTest.ttf since that's generated with
- # AppleStandardEncoding since the same .sfd needs to generate
- # a .pfb file, NameTest.ttf uses a Unicode encoding
-
- # we were using an unsigned char to store a unicode character
- # https://rt.cpan.org/Ticket/Display.html?id=7949
- $exfont = Imager::Font->new(file=>'fontfiles/NameTest.ttf',
- type=>'ft2');
- SKIP:
- {
- ok($exfont, "load TTF via FT2")
- or skip("could not load TTF with FT2", 1);
- my $text = pack("C*", 0xE2, 0x80, 0x90); # "\x{2010}" as utf-8
- my @names = $exfont->glyph_names(string=>$text,
- utf8=>1, reliable_only=>0);
- is($names[0], "hyphentwo", "check utf8 glyph name");
- }
- }
-
- # check that error codes are translated correctly
- my $errfont = Imager::Font->new(file=>"t/t38ft2font.t", type=>"ft2");
- is($errfont, undef, "new font vs non font");
- cmp_ok(Imager->errstr, '=~', qr/unknown file format/, "check error message");
-
- # Multiple Master tests
- # we check a non-MM font errors correctly
- print "# check that the methods act correctly for a non-MM font\n";
- ok(!$exfont->is_mm, "exfont not MM");
- ok((() = $exfont->mm_axes) == 0, "exfont has no MM axes");
- cmp_ok(Imager->errstr, '=~', qr/no multiple masters/,
- "and returns correct error when we ask");
- ok(!$exfont->set_mm_coords(coords=>[0, 0]), "fail setting axis on exfont");
- cmp_ok(Imager->errstr, '=~', qr/no multiple masters/,
- "and returns correct error when we ask");
-
- # try a MM font now - test font only has A defined
- print "# Try a multiple master font\n";
- my $mmfont = Imager::Font->new(file=>"fontfiles/MMOne.pfb", type=>"ft2",
- color=>"white", aa=>1, size=>60);
- ok($mmfont, "loaded MM font");
- ok($mmfont->is_mm, "font is multiple master");
- my @axes = $mmfont->mm_axes;
- is(@axes, 2, "check we got both axes");
- is($axes[0][0], "Weight", "name of first axis");
- is($axes[0][1], 50, "min for first axis");
- is($axes[0][2], 999, "max for first axis");
- is($axes[1][0], "Slant", "name of second axis");
- is($axes[1][1], 0, "min for second axis");
- is($axes[1][2], 999, "max for second axis");
- my $mmim = Imager->new(xsize=>200, ysize=>200);
- $mmim->string(font=>$mmfont, x=>0, 'y'=>50, text=>"A");
- ok($mmfont->set_mm_coords(coords=>[ 700, 0 ]), "set to bold, unsloped");
- $mmim->string(font=>$mmfont, x=>0, 'y'=>100, text=>"A", color=>'blue');
- my @weights = qw(50 260 525 760 999);
- my @slants = qw(0 333 666 999);
- for my $windex (0 .. $#weights) {
- my $weight = $weights[$windex];
- for my $sindex (0 .. $#slants) {
- my $slant = $slants[$sindex];
- $mmfont->set_mm_coords(coords=>[ $weight, $slant ]);
- $mmim->string(font=>$mmfont, x=>30+32*$windex, 'y'=>50+45*$sindex,
- text=>"A");
- }
- }
-
- ok($mmim->write(file=>"testout/t38mm.ppm"), "save MM output");
-
- SKIP:
- { print "# alignment tests\n";
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- ok($font, "loaded deffont OO")
- or skip("could not load font:".Imager->errstr, 4);
- my $im = Imager->new(xsize=>140, ysize=>150);
- my %common =
- (
- font=>$font,
- size=>40,
- aa=>1,
- );
- $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
- $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
- $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
- for my $args ([ x=>5, text=>"A", color=>"white" ],
- [ x=>40, text=>"y", color=>"white" ],
- [ x=>75, text=>"A", channel=>1 ],
- [ x=>110, text=>"y", channel=>1 ]) {
- ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
- ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
- ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
- }
- ok($im->write(file=>'testout/t38align.ppm'), "save align image");
- }
-
-
- { # outputting a space in non-AA could either crash
- # or fail (ft 2.2+)
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- my $im = Imager->new(xsize => 100, ysize => 100);
- ok($im->string(x => 10, y => 10, string => "test space", aa => 0,
- color => '#FFF', size => 8, font => $font),
- "draw space non-antialiased (color)");
- ok($im->string(x => 10, y => 50, string => "test space", aa => 0,
- channel => 0, size => 8, font => $font),
- "draw space non-antialiased (channel)");
- }
-
- { # cannot output "0"
- # https://rt.cpan.org/Ticket/Display.html?id=21770
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- ok($font, "loaded imugly");
- my $imbase = Imager->new(xsize => 100, ysize => 100);
- my $im = $imbase->copy;
- ok($im->string(x => 10, y => 50, string => "0", aa => 0,
- color => '#FFF', size => 20, font => $font),
- "draw '0'");
- ok(Imager::i_img_diff($im->{IMG}, $imbase->{IMG}),
- "make sure we actually drew it");
- $im = $imbase->copy;
- ok($im->string(x => 10, y => 50, string => 0.0, aa => 0,
- color => '#FFF', size => 20, font => $font),
- "draw 0.0");
- ok(Imager::i_img_diff($im->{IMG}, $imbase->{IMG}),
- "make sure we actually drew it");
- }
- { # string output cut off at NUL ('\0')
- # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- ok($font, "loaded imugly");
-
- diff_text_with_nul("a\\0b vs a", "a\0b", "a",
- font => $font, color => '#FFFFFF');
- diff_text_with_nul("a\\0b vs a", "a\0b", "a",
- font => $font, channel => 1);
-
- # UTF8 encoded \x{2010}
- my $dash = pack("C*", 0xE2, 0x80, 0x90);
- diff_text_with_nul("utf8 dash\0dash vs dash", "$dash\0$dash", $dash,
- font => $font, color => '#FFFFFF', utf8 => 1);
- diff_text_with_nul("utf8 dash\0dash vs dash", "$dash\0$dash", $dash,
- font => $font, channel => 1, utf8 => 1);
- }
-
- { # RT 11972
- # when rendering to a transparent image the coverage should be
- # expressed in terms of the alpha channel rather than the color
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- my $im = Imager->new(xsize => 40, ysize => 20, channels => 4);
- ok($im->string(string => "AB", size => 20, aa => 1, color => '#F00',
- x => 0, y => 15, font => $font),
- "draw to transparent image");
- my $im_noalpha = $im->convert(preset => 'noalpha');
- my $im_pal = $im->to_paletted(make_colors => 'mediancut');
- my @colors = $im_pal->getcolors;
- is(@colors, 2, "should be only 2 colors");
- @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
- is_color3($colors[0], 0, 0, 0, "check we got black");
- is_color3($colors[1], 255, 0, 0, "and red");
- }
-
- { # RT 27546
- my $im = Imager->new(xsize => 100, ysize => 100, channels => 4);
- $im->box(filled => 1, color => '#ff0000FF');
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- ok($im->string(x => 0, 'y' => 40, text => 'test',
- size => 11, sizew => 11, font => $font, aa => 1),
- 'draw on translucent image')
- }
-
- { # RT 60199
- # not ft2 specific, but Imager
- my $im = Imager->new(xsize => 100, ysize => 100);
- my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'ft2');
- my $imcopy = $im->copy;
- ok($im, "make test image");
- ok($font, "make test font");
- ok($im->align_string(valign => "center", halign => "center",
- x => 50, y => 50, string => "0", color => "#FFF",
- font => $font),
- "draw 0 aligned");
- ok(Imager::i_img_diff($im->{IMG}, $imcopy->{IMG}),
- "make sure we drew the '0'");
- }
-
- SKIP:
- { # RT 60509
- # checks that a c:foo or c:\foo path is handled correctly on win32
- my $type = "ft2";
- $^O eq "MSWin32" || $^O eq "cygwin"
- or skip("only for win32", 2);
- my $dir = getcwd
- or skip("Cannot get cwd", 2);
- if ($^O eq "cygwin") {
- $dir = Cygwin::posix_to_win_path($dir);
- }
- my $abs_path = abs_path($deffont);
- my $font = Imager::Font->new(file => $abs_path, type => $type);
- ok($font, "found font by absolute path")
- or print "# path $abs_path\n";
- undef $font;
-
- $^O eq "cygwin"
- and skip("cygwin doesn't support drive relative DOSsish paths", 1);
- my ($drive) = $dir =~ /^([a-z]:)/i
- or skip("cwd has no drive letter", 2);
- my $drive_path = $drive . $deffont;
- $font = Imager::Font->new(file => $drive_path, type => $type);
- ok($font, "found font by drive relative path")
- or print "# path $drive_path\n";
- }
-
-}
-
-sub align_test {
- my ($h, $v, $x, $y, $f, $img) = @_;
-
- my @pos = $f->align(halign=>$h, valign=>$v, 'x'=>$x, 'y'=>$y,
- image=>$img, size=>15, color=>'FFFFFF',
- string=>"x$h ${v}y", channel=>1, aa=>1);
- @pos = $img->align_string(halign=>$h, valign=>$v, 'x'=>$x, 'y'=>$y,
- font=>$f, size=>15, color=>'FF99FF',
- string=>"x$h ${v}y", aa=>1);
- if (ok(@pos == 4, "$h $v aligned output")) {
- # checking corners
- my $cx = int(($pos[0] + $pos[2]) / 2);
- my $cy = int(($pos[1] + $pos[3]) / 2);
-
- print "# @pos cx $cx cy $cy\n";
- okmatchcolor($img, $cx, $pos[1]-1, @base_color, "outer top edge");
- okmatchcolor($img, $cx, $pos[3], @base_color, "outer bottom edge");
- okmatchcolor($img, $pos[0]-1, $cy, @base_color, "outer left edge");
- okmatchcolor($img, $pos[2], $cy, @base_color, "outer right edge");
-
- okmismatchcolor($img, $cx, $pos[1], @base_color, "inner top edge");
- okmismatchcolor($img, $cx, $pos[3]-1, @base_color, "inner bottom edge");
- okmismatchcolor($img, $pos[0], $cy, @base_color, "inner left edge");
-# okmismatchcolor($img, $pos[2]-1, $cy, @base_color, "inner right edge");
-# XXX: This gets triggered by a freetype2 bug I think
-# $ rpm -qa | grep freetype
-# freetype-2.1.3-6
-#
-# (addi: 4/1/2004).
-
- cross($img, $x, $y, 'FF0000');
- cross($img, $cx, $pos[1]-1, '0000FF');
- cross($img, $cx, $pos[3], '0000FF');
- cross($img, $pos[0]-1, $cy, '0000FF');
- cross($img, $pos[2], $cy, '0000FF');
- }
- else {
- SKIP: { skip("couldn't draw text", 7) };
- }
-}
-
-sub okmatchcolor {
- my ($img, $x, $y, $r, $g, $b, $about) = @_;
-
- my $c = $img->getpixel('x'=>$x, 'y'=>$y);
- my ($fr, $fg, $fb) = $c->rgba;
- ok($fr == $r && $fg == $g && $fb == $b,
- "want ($r,$g,$b) found ($fr,$fg,$fb)\@($x,$y) $about");
-}
-
-sub okmismatchcolor {
- my ($img, $x, $y, $r, $g, $b, $about) = @_;
-
- my $c = $img->getpixel('x'=>$x, 'y'=>$y);
- my ($fr, $fg, $fb) = $c->rgba;
- ok($fr != $r || $fg != $g || $fb != $b,
- "don't want ($r,$g,$b) found ($fr,$fg,$fb)\@($x,$y) $about");
-}
-
-sub cross {
- my ($img, $x, $y, $color) = @_;
-
- $img->setpixel('x'=>[$x, $x, $x, $x, $x, $x-2, $x-1, $x+1, $x+2],
- 'y'=>[$y-2, $y-1, $y, $y+1, $y+2, $y, $y, $y, $y],
- color => $color);
-
-}
postfix
infix
unary
+Uncategorized
+Blit
/;
local %Pod::Wordlist::Wordlist = %Pod::Wordlist::Wordlist;
Imager::ImgRaw T_IMAGER_IMAGE
Imager::Font::TT T_PTROBJ
Imager::IO T_PTROBJ
-Imager::Font::FT2 T_PTROBJ
Imager::FillHandle T_PTROBJ
Imager::Internal::Hlines T_PTROBJ
const char * T_PV