0.39 pre1
- split Imager::Font into a base, *::Type1 and *::Truetype
+ - writing faxable tiff now allows 2 and 4 channel images
+ - virtual image interface - 8-bit/sample images
+ - paletted images
+ - 16-bit/sample images
+ - masked images
+ - writing non-8bit images to raw
+ - writing '' '' to tiff
+ - i_convert support for high-bit images and paletted images
+ - i_copy for high-bit and paletted images
+ - tests for rubthru
+ - rubthru can now rub a 2 channel image over a 1 or 3 channel
+ image
+ - rubthru for high-bit images
+ - i_readgif_multi functions, which return all the frames from a
+ GIF file, along with a bunch of meta-information as tag
+ - OO interface to tags
+ - OO interface read_multi() to read multi-image files (just GIF
+ so far)
+ - documentation for the multi-image GIF reading and tags
+ - rotate() method with rotate by steps of 90 degrees (and docs)
+ - fixed a bug in i_img_pal_new_low()
+ - added gaussian to the filters list
+ - documented the individual filters
+ - fixed the right-hand side of bounding boxes for TT fonts
+ where the last character overlaps the advance width (eg.
+ italic fonts)
+ - added rotation by exact amounts in degrees or radians,
+ implemented using the matrix idea from Addi. The procedural
+ matrix transformer is exposed, but still needs testing (as XS)
+ and needs an OO interface, hopefully with some helper tools,
+ like the preset interface with ->convert().
+ - MY::postamble() in Makefile.PL included a broken rule
+ (Makefile.PL 1.13.2.5)
+ - support for GDI fonts under Win32
+ - made that work for cygwin too (needs the w32api package)
+ - freetype1 support under Win32
+ - t1lib support under Win32
+ - fixed a minor bug in font.c (invalid font files caused a SEGV)
+ - checked cygwin for both t1lib and freetype1
+ - freetype 2 support
+ - exposed the matrix_transform() function to the OO interface
+ - added Imager::Matrix2d convenience class
+ - support for setting the resolution when writing to PNG
+ - retrieve physical resolution to tags when reading from PNG
+ - found an XS bug in the interface to i_tags_add()
+ - fixed handling of writing text to a channel with freetype2
+ (sometimes the edge of a character would damage the edge of the
+ previous character)
+ - some utf8 support for freetype2
+ - some vertical layout support for freetype2
+ - named parameters for specifying colors, with quite a few options.
=================================================================
package Imager;
-
-
use strict;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS);
use IO::File;
i_t1_text
i_t1_bbox
-
i_tt_set_aa
i_tt_cp
i_tt_text
NF
);
-
-
@EXPORT=qw(
init_log
i_list_formats
unload_plugin
)]);
-
BEGIN {
require Exporter;
require DynaLoader;
BEGIN {
i_init_fonts(); # Initialize font engines
+ Imager::Font::__init();
for(i_list_formats()) { $formats{$_}++; }
if ($formats{'t1'}) {
i_t1_set_aa(1);
}
- if (!$formats{'t1'} and !$formats{'tt'}) {
+ if (!$formats{'t1'} and !$formats{'tt'}
+ && !$formats{'ft2'} && !$formats{'w32'}) {
$fontstate='no font support';
}
defaults => { },
callsub => sub { my %hsh=@_; i_nearest_color($hsh{image}, $hsh{xo}, $hsh{yo}, $hsh{colors}, $hsh{dist}); }
};
+ $filters{gaussian} = {
+ callseq => [ 'image', 'stddev' ],
+ defaults => { },
+ callsub => sub { my %hsh = @_; i_gaussian($hsh{image}, $hsh{stddev}); },
+ };
$FORMATGUESS=\&def_guess_type;
}
return join(": ", map $_->[0], i_errors());
}
-
#
# Methods to be called on objects.
#
sub img_set {
my $self=shift;
- my %hsh=(xsize=>100,ysize=>100,channels=>3,@_);
+ my %hsh=(xsize=>100, ysize=>100, channels=>3, bits=>8, type=>'direct', @_);
if (defined($self->{IMG})) {
- i_img_destroy($self->{IMG});
+ # let IIM_DESTROY destroy it, it's possible this image is
+ # referenced from a virtual image (like masked)
+ #i_img_destroy($self->{IMG});
undef($self->{IMG});
}
- $self->{IMG}=Imager::ImgRaw::new($hsh{'xsize'},$hsh{'ysize'},$hsh{'channels'});
+ if ($hsh{type} eq 'paletted' || $hsh{type} eq 'pseudo') {
+ $self->{IMG} = i_img_pal_new($hsh{xsize}, $hsh{ysize}, $hsh{channels},
+ $hsh{maxcolors} || 256);
+ }
+ elsif ($hsh{bits} == 16) {
+ $self->{IMG} = i_img_16_new($hsh{xsize}, $hsh{ysize}, $hsh{channels});
+ }
+ else {
+ $self->{IMG}=Imager::ImgRaw::new($hsh{'xsize'}, $hsh{'ysize'},
+ $hsh{'channels'});
+ }
+}
+
+# created a masked version of the current image
+sub masked {
+ my $self = shift;
+
+ $self or return undef;
+ my %opts = (left => 0,
+ top => 0,
+ right => $self->getwidth,
+ bottom => $self->getheight,
+ @_);
+ my $mask = $opts{mask} ? $opts{mask}{IMG} : undef;
+
+ my $result = Imager->new;
+ $result->{IMG} = i_img_masked_new($self->{IMG}, $mask, $opts{left},
+ $opts{top}, $opts{right} - $opts{left},
+ $opts{bottom} - $opts{top});
+ # keep references to the mask and base images so they don't
+ # disappear on us
+ $result->{DEPENDS} = [ $self->{IMG}, $mask ];
+
+ $result;
+}
+
+# convert an RGB image into a paletted image
+sub to_paletted {
+ my $self = shift;
+ my $opts;
+ if (@_ != 1 && !ref $_[0]) {
+ $opts = { @_ };
+ }
+ else {
+ $opts = shift;
+ }
+
+ my $result = Imager->new;
+ $result->{IMG} = i_img_to_pal($self->{IMG}, $opts);
+
+ #print "Type ", i_img_type($result->{IMG}), "\n";
+
+ $result->{IMG} or undef $result;
+
+ return $result;
+}
+
+# convert a paletted (or any image) to an 8-bit/channel RGB images
+sub to_rgb8 {
+ my $self = shift;
+ my $result;
+
+ if ($self->{IMG}) {
+ $result = Imager->new;
+ $result->{IMG} = i_img_to_rgb($self->{IMG})
+ or undef $result;
+ }
+
+ return $result;
+}
+
+sub addcolors {
+ my $self = shift;
+ my %opts = (colors=>[], @_);
+
+ @{$opts{colors}} or return undef;
+
+ $self->{IMG} and i_addcolors($self->{IMG}, @{$opts{colors}});
+}
+
+sub setcolors {
+ my $self = shift;
+ my %opts = (start=>0, colors=>[], @_);
+ @{$opts{colors}} or return undef;
+
+ $self->{IMG} and i_setcolors($self->{IMG}, $opts{start}, @{$opts{colors}});
+}
+
+sub getcolors {
+ my $self = shift;
+ my %opts = @_;
+ if (!exists $opts{start} && !exists $opts{count}) {
+ # get them all
+ $opts{start} = 0;
+ $opts{count} = $self->colorcount;
+ }
+ elsif (!exists $opts{count}) {
+ $opts{count} = 1;
+ }
+ elsif (!exists $opts{start}) {
+ $opts{start} = 0;
+ }
+
+ $self->{IMG} and
+ return i_getcolors($self->{IMG}, $opts{start}, $opts{count});
+}
+
+sub colorcount {
+ i_colorcount($_[0]{IMG});
+}
+
+sub maxcolors {
+ i_maxcolors($_[0]{IMG});
+}
+
+sub findcolor {
+ my $self = shift;
+ my %opts = @_;
+ $opts{color} or return undef;
+
+ $self->{IMG} and i_findcolor($self->{IMG}, $opts{color});
+}
+
+sub bits {
+ my $self = shift;
+ $self->{IMG} and i_img_bits($self->{IMG});
+}
+
+sub type {
+ my $self = shift;
+ if ($self->{IMG}) {
+ return i_img_type($self->{IMG}) ? "paletted" : "direct";
+ }
+}
+
+sub virtual {
+ my $self = shift;
+ $self->{IMG} and i_img_virtual($self->{IMG});
+}
+
+sub tags {
+ my ($self, %opts) = @_;
+
+ $self->{IMG} or return;
+
+ if (defined $opts{name}) {
+ my @result;
+ my $start = 0;
+ my $found;
+ while (defined($found = i_tags_find($self->{IMG}, $opts{name}, $start))) {
+ push @result, (i_tags_get($self->{IMG}, $found))[1];
+ $start = $found+1;
+ }
+ return wantarray ? @result : $result[0];
+ }
+ elsif (defined $opts{code}) {
+ my @result;
+ my $start = 0;
+ my $found;
+ while (defined($found = i_tags_findn($self->{IMG}, $opts{code}, $start))) {
+ push @result, (i_tags_get($self->{IMG}, $found))[1];
+ $start = $found+1;
+ }
+ return @result;
+ }
+ else {
+ if (wantarray) {
+ return map { [ i_tags_get($self->{IMG}, $_) ] } 0.. i_tags_count($self->{IMG})-1;
+ }
+ else {
+ return i_tags_count($self->{IMG});
+ }
+ }
+}
+
+sub addtag {
+ my $self = shift;
+ my %opts = @_;
+
+ return -1 unless $self->{IMG};
+ if ($opts{name}) {
+ if (defined $opts{value}) {
+ if ($opts{value} =~ /^\d+$/) {
+ # add as a number
+ return i_tags_addn($self->{IMG}, $opts{name}, 0, $opts{value});
+ }
+ else {
+ return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{value}, 0);
+ }
+ }
+ elsif (defined $opts{data}) {
+ # force addition as a string
+ return i_tags_add($self->{IMG}, $opts{name}, 0, $opts{data}, 0);
+ }
+ else {
+ $self->{ERRSTR} = "No value supplied";
+ return undef;
+ }
+ }
+ elsif ($opts{code}) {
+ if (defined $opts{value}) {
+ if ($opts{value} =~ /^\d+$/) {
+ # add as a number
+ return i_tags_addn($self->{IMG}, $opts{code}, 0, $opts{value});
+ }
+ else {
+ return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{value}, 0);
+ }
+ }
+ elsif (defined $opts{data}) {
+ # force addition as a string
+ return i_tags_add($self->{IMG}, $opts{code}, 0, $opts{data}, 0);
+ }
+ else {
+ $self->{ERRSTR} = "No value supplied";
+ return undef;
+ }
+ }
+ else {
+ return undef;
+ }
+}
+
+sub deltag {
+ my $self = shift;
+ my %opts = @_;
+
+ return 0 unless $self->{IMG};
+
+ if (defined $opts{index}) {
+ return i_tags_delete($self->{IMG}, $opts{index});
+ }
+ elsif (defined $opts{name}) {
+ return i_tags_delbyname($self->{IMG}, $opts{name});
+ }
+ elsif (defined $opts{code}) {
+ return i_tags_delbycode($self->{IMG}, $opts{code});
+ }
+ else {
+ $self->{ERRSTR} = "Need to supply index, name, or code parameter";
+ return 0;
+ }
}
# Read an image from file
my ($fh, $fd, $IO);
if (defined($self->{IMG})) {
- i_img_destroy($self->{IMG});
+ # let IIM_DESTROY do the destruction, since the image may be
+ # referenced from elsewhere
+ #i_img_destroy($self->{IMG});
undef($self->{IMG});
}
} else {
$fh = new IO::File($input{file},"w+");
if (!defined $fh) { $self->{ERRSTR}='Could not open file'; return undef; }
- binmode($fh);
+ binmode($fh) or die;
$fd = $fh->fileno();
}
-
-
if ($iolready{$input{type}}) {
if (defined $fd) {
$IO = io_new_fd($fd);
}
}
+# read multiple images from a file
+sub read_multi {
+ my ($class, %opts) = @_;
+
+ if ($opts{file} && !exists $opts{type}) {
+ # guess the type
+ my $type = $FORMATGUESS->($opts{file});
+ $opts{type} = $type;
+ }
+ unless ($opts{type}) {
+ $ERRSTR = "No type parameter supplied and it couldn't be guessed";
+ return;
+ }
+ my $fd;
+ my $file;
+ if ($opts{file}) {
+ $file = IO::File->new($opts{file}, "r");
+ unless ($file) {
+ $ERRSTR = "Could not open file $opts{file}: $!";
+ return;
+ }
+ binmode $file;
+ $fd = fileno($file);
+ }
+ elsif ($opts{fh}) {
+ $fd = fileno($opts{fh});
+ unless ($fd) {
+ $ERRSTR = "File handle specified with fh option not open";
+ return;
+ }
+ }
+ elsif ($opts{fd}) {
+ $fd = $opts{fd};
+ }
+ elsif ($opts{callback} || $opts{data}) {
+ # don't fail here
+ }
+ else {
+ $ERRSTR = "You need to specify one of file, fd, fh, callback or data";
+ return;
+ }
+
+ if ($opts{type} eq 'gif') {
+ my @imgs;
+ if ($fd) {
+ @imgs = i_readgif_multi($fd);
+ }
+ else {
+ if (Imager::i_giflib_version() < 4.0) {
+ $ERRSTR = "giflib3.x does not support callbacks";
+ return;
+ }
+ if ($opts{callback}) {
+ @imgs = i_readgif_multi_callback($opts{callback})
+ }
+ else {
+ @imgs = i_readgif_multi_scalar($opts{data});
+ }
+ }
+ if (@imgs) {
+ return map {
+ bless { IMG=>$_, DEBUG=>$DEBUG, ERRSTR=>undef }, 'Imager'
+ } @imgs;
+ }
+ else {
+ $ERRSTR = _error_as_msg();
+ return;
+ }
+ }
+
+ $ERRSTR = "Cannot read multiple images from $opts{type} files";
+ return;
+}
+
# Destroy an Imager object
sub DESTROY {
my $self=shift;
# delete $instances{$self};
if (defined($self->{IMG})) {
- i_img_destroy($self->{IMG});
+ # the following is now handled by the XS DESTROY method for
+ # Imager::ImgRaw object
+ # Re-enabling this will break virtual images
+ # tested for in t/t020masked.t
+ # i_img_destroy($self->{IMG});
undef($self->{IMG});
} else {
# print "Destroy Called on an empty image!\n"; # why did I put this here??
}
}
-
-
-
-
-
-
-
sub rubthrough {
my $self=shift;
my %opts=(tx=>0,ty=>0,@_);
unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
unless ($opts{src} && $opts{src}->{IMG}) { $self->{ERRSTR}='empty input image for source'; return undef; }
- i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx},$opts{ty});
+ unless (i_rubthru($self->{IMG}, $opts{src}->{IMG}, $opts{tx},$opts{ty})) {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return undef;
+ }
return $self;
}
return ();
}
+sub rotate {
+ my $self = shift;
+ my %opts = @_;
+ if (defined $opts{right}) {
+ my $degrees = $opts{right};
+ if ($degrees < 0) {
+ $degrees += 360 * int(((-$degrees)+360)/360);
+ }
+ $degrees = $degrees % 360;
+ if ($degrees == 0) {
+ return $self->copy();
+ }
+ elsif ($degrees == 90 || $degrees == 180 || $degrees == 270) {
+ my $result = Imager->new();
+ if ($result->{IMG} = i_rotate90($self->{IMG}, $degrees)) {
+ return $result;
+ }
+ else {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return undef;
+ }
+ }
+ else {
+ $self->{ERRSTR} = "Parameter 'right' must be a multiple of 90 degrees";
+ return undef;
+ }
+ }
+ elsif (defined $opts{radians} || defined $opts{degrees}) {
+ my $amount = $opts{radians} || $opts{degrees} * 3.1415926535 / 180;
+
+ my $result = Imager->new;
+ if ($result->{IMG} = i_rotate_exact($self->{IMG}, $amount)) {
+ return $result;
+ }
+ else {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return undef;
+ }
+ }
+ else {
+ $self->{ERRSTR} = "Only the 'right' parameter is available";
+ return undef;
+ }
+}
+
+sub matrix_transform {
+ my $self = shift;
+ my %opts = @_;
+
+ if ($opts{matrix}) {
+ my $xsize = $opts{xsize} || $self->getwidth;
+ my $ysize = $opts{ysize} || $self->getheight;
+ my $result = Imager->new;
+ $result->{IMG} = i_matrix_transform($self->{IMG}, $xsize, $ysize,
+ $opts{matrix})
+ or return undef;
+
+ return $result;
+ }
+ else {
+ $self->{ERRSTR} = "matrix parameter required";
+ return undef;
+ }
+}
+
+# blame Leolo :)
+*yatf = \&matrix_transform;
# These two are supported for legacy code only
sub i_color_new {
- return Imager::Color->new($_[0], $_[1], $_[2], $_[3]);
+ return Imager::Color->new(@_);
}
sub i_color_set {
- return Imager::Color::set($_[0], $_[1], $_[2], $_[3], $_[4]);
+ return Imager::Color::set(@_);
}
-
-
# Draws a box between the specified corner points.
-
sub box {
my $self=shift;
unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
return $self;
}
-
-
-
-
-
-
-
-
-
-
-
# destructive border - image is shrunk by one pixel all around
sub border {
return;
}
- $input{font}->draw(image=>$self, %input);
+ unless ($input{font}->draw(image=>$self, %input)) {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return;
+ }
return $self;
}
-
-
-
-
# Shortcuts that can be exported
sub newcolor { Imager::Color->new(@_); }
#### Utility routines
-sub errstr { $_[0]->{ERRSTR} }
-
-
-
-
-
+sub errstr {
+ ref $_[0] ? $_[0]->{ERRSTR} : $ERRSTR
+}
# Default guess for the type of an image from extension
return (caption=>$caption,photogr=>$photogr,headln=>$headln,credit=>$credit);
}
-
-
-
-
-
# Autoload methods go after =cut, and are processed by the autosplit program.
1;
$img->img_set(xsize=>500, ysize=>500, channels=>4);
+To create paletted images, set the 'type' parameter to 'paletted':
+
+ $img = Imager->new(xsize=>200, ysize=>200, channels=>3, type=>'paletted');
+
+which creates an image with a maxiumum of 256 colors, which you can
+change by supplying the C<maxcolors> parameter.
+
+You can create a new paletted image from an existing image using the
+to_paletted() method:
+
+ $palimg = $img->to_paletted(\%opts)
+
+where %opts contains the options specified under L<Quantization options>.
+
+You can convert a paletted image (or any image) to an 8-bit/channel
+RGB image with:
+
+ $rgbimg = $img->to_rgb8;
+
+Warning: if you draw on a paletted image with colors that aren't in
+the palette, the image will be internally converted to a normal image.
+
+For improved color precision you can use the bits parameter to specify
+16 bites per channel:
+
+ $img = Imager->new(xsize=>200, ysize=>200, channels=>3, bits=>16);
+
+Note that as of this writing all functions should work on 16-bit
+images, but at only 8-bit/channel precision.
+
+Currently only 8 and 16/bit per channel image types are available,
+this may change later.
+
Color objects are created by calling the Imager::Color->new()
method:
@images)
or die "Oh dear!";
+You can read multi-image files (currently only GIF files) using the
+read_multi() method:
+
+ my @imgs = Imager->read_multi(file=>'foo.gif')
+ or die "Cannot read images: ",Imager->errstr;
+
+The possible parameters for read_multi() are:
+
+=over
+
+=item file
+
+The name of the file to read in.
+
+=item fh
+
+A filehandle to read in. This can be the name of a filehandle, but it
+will need the package name, no attempt is currently made to adjust
+this to the caller's package.
+
+=item fd
+
+The numeric file descriptor of an open file (or socket).
+
+=item callback
+
+A function to be called to read in data, eg. reading a blob from a
+database incrementally.
+
+=item data
+
+The data of the input file in memory.
+
+=item type
+
+The type of file. If the file is parameter is given and provides
+enough information to guess the type, then this parameter is optional.
+
+=back
+
+Note: you cannot use the callback or data parameter with giflib
+versions before 4.0.
+
+When reading from a GIF file with read_multi() the images are returned
+as paletted images.
+
=head2 Gif options
These options can be specified when calling write_multi() for gif
print "Less than 512 colors in image\n";
}
+The bits() method retrieves the number of bits used to represent each
+channel in a pixel, typically 8. The type() method returns either
+'direct' for truecolor images or 'paletted' for paletted images. The
+virtual() method returns non-zero if the image contains no actual
+pixels, for example masked images.
+
+=head2 Paletted Images
+
+In general you can work with paletted images in the same way as RGB
+images, except that if you attempt to draw to a paletted image with a
+color that is not in the image's palette, the image will be converted
+to an RGB image. This means that drawing on a paletted image with
+anti-aliasing enabled will almost certainly convert the image to RGB.
+
+You can add colors to a paletted image with the addcolors() method:
+
+ my @colors = ( Imager::Color->new(255, 0, 0),
+ Imager::Color->new(0, 255, 0) );
+ my $index = $img->addcolors(colors=>\@colors);
+
+The return value is the index of the first color added, or undef if
+adding the colors would overflow the palette.
+
+Once you have colors in the palette you can overwrite them with the
+setcolors() method:
+
+ $img->setcolors(start=>$start, colors=>\@colors);
+
+Returns true on success.
+
+To retrieve existing colors from the palette use the getcolors() method:
+
+ # get the whole palette
+ my @colors = $img->getcolors();
+ # get a single color
+ my $color = $img->getcolors(start=>$index);
+ # get a range of colors
+ my @colors = $img->getcolors(start=>$index, count=>$count);
+
+To quickly find a color in the palette use findcolor():
+
+ my $index = $img->findcolor(color=>$color);
+
+which returns undef on failure, or the index of the color.
+
+You can get the current palette size with $img->colorcount, and the
+maximum size of the palette with $img->maxcolors.
+
=head2 Drawing Methods
IMPLEMENTATION MORE OR LESS DONE CHECK THE TESTS
$img->flip(dir=>"vh"); # vertical and horizontal flip
$nimg = $img->copy->flip(dir=>"v"); # make a copy and flip it vertically
+=head2 Rotating images
+
+Use the rotate() method to rotate an image.
+
+To rotate by an exact amount in degrees or radians, use the 'degrees'
+or 'radians' parameter:
+
+ my $rot20 = $img->rotate(degrees=>20);
+ my $rotpi4 = $img->rotate(radians=>3.14159265/4);
+
+To rotate in steps of 90 degrees, use the 'right' parameter:
+
+ my $rotated = $img->rotate(right=>270);
+
+Rotations are clockwise for positive values.
+
=head2 Blending Images
To put an image or a part of an image directly
$img->rubthrough(src=>$srcimage,tx=>30,ty=>50);
-That will take the image C<$srcimage> and overlay it with the
-upper left corner at (30,50). The C<$srcimage> must be a 4 channel
-image. The last channel is used as an alpha channel.
+That will take the image C<$srcimage> and overlay it with the upper
+left corner at (30,50). You can rub 2 or 4 channel images onto a 3
+channel image, or a 2 channel image onto a 1 channel image. The last
+channel is used as an alpha channel.
=head2 Filters
source.
Filter Arguments
- turbnoise
autolevels lsat(0.1) usat(0.1) skew(0)
- radnoise
- noise amount(3) subtype(0)
contrast intensity
- hardinvert
+ conv coef
+ gaussian stddev
gradgen xo yo colors dist
+ hardinvert
+ noise amount(3) subtype(0)
+ radnoise xo(100) yo(100) ascale(17.0) rscale(0.02)
+ turbnoise xo(0.0) yo(0.0) scale(10.0)
The default values are in parenthesis. All parameters must have some
value but if a parameter has a default value it may be omitted when
calling the filter function.
-FIXME: make a seperate pod for filters?
+The filters are:
+
+=over
+
+=item autolevels
+
+scales the value of each channel so that the values in the image will
+cover the whole possible range for the channel. I<lsat> and I<usat>
+truncate the range by the specified fraction at the top and bottom of
+the range respectivly..
+
+=item contrast
+
+scales each channel by I<intensity>. Values of I<intensity> < 1.0
+will reduce the contrast.
+
+=item conv
+
+performs 2 1-dimensional convolutions on the image using the values
+from I<coef>. I<coef> should be have an odd length.
+
+=item gaussian
+
+performs a gaussian blur of the image, using I<stddev> as the standard
+deviation of the curve used to combine pixels, larger values give
+bigger blurs. For a definition of Gaussian Blur, see:
+
+ http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node99.html
+
+=item gradgen
+
+renders a gradient, with the given I<colors> at the corresponding
+points (x,y) in I<xo> and I<yo>. You can specify the way distance is
+measured for color blendeing by setting I<dist> to 0 for Euclidean, 1
+for Euclidean squared, and 2 for Manhattan distance.
+
+=item hardinvert
+
+inverts the image, black to white, white to black. All channels are
+inverted, including the alpha channel if any.
+
+=item noise
+
+adds noise of the given I<amount> to the image. If I<subtype> is
+zero, the noise is even to each channel, otherwise noise is added to
+each channel independently.
+
+=item radnoise
+
+renders radiant Perlin turbulent noise. The centre of the noise is at
+(I<xo>, I<yo>), I<ascale> controls the angular scale of the noise ,
+and I<rscale> the radial scale, higher numbers give more detail.
+
+=item turbnoise
+
+renders Perlin turbulent noise. (I<xo>, I<yo>) controls the origin of
+the noise, and I<scale> the scale of the noise, with lower numbers
+giving more detail.
+
+=back
+
+A demonstration of the the filters can be found at:
+
+ http://www.develop-help.com/imager/filters.html
+
+(This is a slow link.)
=head2 Color transformations
the virtual machine used to transform the images, see
L<Imager::regmach.pod>.
+=head2 Matrix Transformations
+
+Rather than having to write code in a little language, you can use a
+matrix to perform transformations, using the matrix_transform()
+method:
+
+ my $im2 = $im->matrix_transform(matrix=>[ -1, 0, $im->getwidth-1,
+ 0, 1, 0,
+ 0, 0, 1 ]);
+
+By default the output image will be the same size as the input image,
+but you can supply the xsize and ysize parameters to change the size.
+
+Rather than building matrices by hand you can use the Imager::Matrix2d
+module to build the matrices. This class has methods to allow you to
+scale, shear, rotate, translate and reflect, and you can combine these
+with an overloaded multiplication operator.
+
+WARNING: the matrix you provide in the matrix operator transforms the
+co-ordinates within the B<destination> image to the co-ordinates
+within the I<source> image. This can be confusing.
+
+Since Imager has 3 different fairly general ways of transforming an
+image spatially, this method also has a yatf() alias. Yet Another
+Transformation Function.
+
+=head2 Masked Images
+
+Masked images let you control which pixels are modified in an
+underlying image. Where the first channel is completely black in the
+mask image, writes to the underlying image are ignored.
+
+For example, given a base image called $img:
+
+ my $mask = Imager->new(xsize=>$img->getwidth, ysize=>getheight,
+ channels=>1);
+ # ... draw something on the mask
+ my $maskedimg = $img->masked(mask=>$mask);
+
+You can specifiy the region of the underlying image that is masked
+using the left, top, right and bottom options.
+
+If you just want a subset of the image, without masking, just specify
+the region without specifying a mask.
+
=head2 Plugins
It is possible to add filters to the module without recompiling the
Linux, Solaris, HPUX, OpenBSD, FreeBSD, TRU64/OSF1, AIX.
If you test this on other systems please let me know.
+=head2 Tags
+
+Image tags contain meta-data about the image, ie. information not
+stored as pixels of the image.
+
+At the perl level each tag has a name or code and a value, which is an
+integer or an arbitrary string. An image can contain more than one
+tag with the same name or code.
+
+You can retrieve tags from an image using the tags() method, you can
+get all of the tags in an image, as a list of array references, with
+the code or name of the tag followed by the value of the tag:
+
+ my @alltags = $img->tags;
+
+or you can get all tags that have a given name:
+
+ my @namedtags = $img->tags(name=>$name);
+
+or a given code:
+
+ my @tags = $img->tags(code=>$code);
+
+You can add tags using the addtag() method, either by name:
+
+ my $index = $img->addtag(name=>$name, value=>$value);
+
+or by code:
+
+ my $index = $img->addtag(code=>$code, value=>$value);
+
+You can remove tags with the deltag() method, either by index:
+
+ $img->deltag(index=>$index);
+
+or by name:
+
+ $img->deltag(name=>$name);
+
+or by code:
+
+ $img->deltag(code=>$code);
+
+In each case deltag() returns the number of tags deleted.
+
+When you read a GIF image using read_multi(), each image can include
+the following tags:
+
+=over
+
+=item gif_left
+
+the offset of the image from the left of the "screen" ("Image Left
+Position")
+
+=item gif_top
+
+the offset of the image from the top of the "screen" ("Image Top Position")
+
+=item gif_interlace
+
+non-zero if the image was interlaced ("Interlace Flag")
+
+=item gif_screen_width
+
+=item gif_screen_height
+
+the size of the logical screen ("Logical Screen Width",
+"Logical Screen Height")
+
+=item gif_local_map
+
+Non-zero if this image had a local color map.
+
+=item gif_background
+
+The index in the global colormap of the logical screen's background
+color. This is only set if the current image uses the global
+colormap.
+
+=item gif_trans_index
+
+The index of the color in the colormap used for transparency. If the
+image has a transparency then it is returned as a 4 channel image with
+the alpha set to zero in this palette entry. ("Transparent Color Index")
+
+=item gif_delay
+
+The delay until the next frame is displayed, in 1/100 of a second.
+("Delay Time").
+
+=item gif_user_input
+
+whether or not a user input is expected before continuing (view dependent)
+("User Input Flag").
+
+=item gif_disposal
+
+how the next frame is displayed ("Disposal Method")
+
+=item gif_loop
+
+the number of loops from the Netscape Loop extension. This may be zero.
+
+=item gif_comment
+
+the first block of the first gif comment before each image.
+
+=back
+
+Where applicable, the ("name") is the name of that field from the GIF89
+standard.
+
+The following ares are set in a TIFF image when read, and can be set
+to control output:
+
+=over
+
+=item tiff_resolutionunit
+
+The value of the ResolutionUnit tag. This is ignored on writing if
+the i_aspect_only tag is non-zero.
+
+=back
+
+Some standard tags will be implemented as time goes by:
+
+=over
+
+=item i_xres
+
+=item i_yres
+
+The spatial resolution of the image in pixels per inch. If the image
+format uses a different scale, eg. pixels per meter, then this value
+is converted. A floating point number stored as a string.
+
+=item i_aspect_only
+
+If this is non-zero then the values in i_xres and i_yres are treated
+as a ratio only. If the image format does not support aspect ratios
+then this is scaled so the smaller value is 72dpi.
+
+=back
+
=head1 BUGS
box, arc, circle do not support antialiasing yet. arc, is only filled
=head1 SEE ALSO
-perl(1), Imager::Color(3), Imager::Font, Affix::Infix2Postfix(3),
-Parse::RecDescent(3) http://www.eecs.umich.edu/~addi/perl/Imager/
+perl(1), Imager::Color(3), Imager::Font(3), Imager::Matrix2d(3),
+Affix::Infix2Postfix(3), Parse::RecDescent(3)
+http://www.eecs.umich.edu/~addi/perl/Imager/
=cut
typedef io_glue* Imager__IO;
typedef i_color* Imager__Color;
+typedef i_fcolor* Imager__Color__Float;
typedef i_img* Imager__ImgRaw;
typedef TT_Fonthandle* Imager__TTHandle;
#endif
+#ifdef HAVE_FT2
+typedef FT2_Fonthandle* Imager__Font__FT2;
+#endif
+
typedef struct i_reader_data_tag
{
/* presumably a CODE ref or name of a sub */
}
}
+/* I don't think ICLF_* names belong at the C interface
+ this makes the XS code think we have them, to let us avoid
+ putting function bodies in the XS code
+*/
+#define ICLF_new_internal(r, g, b, a) i_fcolor_new((r), (g), (b), (a))
+#define ICLF_DESTROY(cl) i_fcolor_destroy(cl)
+
MODULE = Imager PACKAGE = Imager::Color PREFIX = ICL_
Imager::Color
+MODULE = Imager PACKAGE = Imager::Color::Float PREFIX=ICLF_
+Imager::Color::Float
+ICLF_new_internal(r, g, b, a)
+ double r
+ double g
+ double b
+ double a
+
+void
+ICLF_DESTROY(cl)
+ Imager::Color::Float cl
+void
+ICLF_rgba(cl)
+ Imager::Color::Float cl
+ PREINIT:
+ int ch;
+ PPCODE:
+ EXTEND(SP, MAXCHANNELS);
+ for (ch = 0; ch < MAXCHANNELS; ++ch) {
+ /* printf("%d: %g\n", ch, cl->channel[ch]); */
+ PUSHs(sv_2mortal(newSVnv(cl->channel[ch])));
+ }
+
+void
+ICLF_set_internal(cl,r,g,b,a)
+ Imager::Color::Float cl
+ double r
+ double g
+ double b
+ double a
+ PPCODE:
+ cl->rgba.r = r;
+ cl->rgba.g = g;
+ cl->rgba.b = b;
+ cl->rgba.a = a;
+ EXTEND(SP, 1);
+ PUSHs(ST(0));
MODULE = Imager PACKAGE = Imager::ImgRaw PREFIX = IIM_
Imager::ImgRaw im
PPCODE:
EXTEND(SP, 1);
- PUSHs(sv_2mortal(newSVpv(im->data, im->bytes)));
+ PUSHs(im->idata ? sv_2mortal(newSVpv(im->idata, im->bytes))
+ : &PL_sv_undef);
void
Imager::ImgRaw src
-void
+undef_int
i_rubthru(im,src,tx,ty)
Imager::ImgRaw im
Imager::ImgRaw src
Imager::ImgRaw im
int direction
+Imager::ImgRaw
+i_rotate90(im, degrees)
+ Imager::ImgRaw im
+ int degrees
+
+Imager::ImgRaw
+i_rotate_exact(im, amount)
+ Imager::ImgRaw im
+ double amount
+
+Imager::ImgRaw
+i_matrix_transform(im, xsize, ysize, matrix)
+ Imager::ImgRaw im
+ int xsize
+ int ysize
+ PREINIT:
+ double matrix[9];
+ AV *av;
+ IV len;
+ SV *sv1;
+ int i;
+ CODE:
+ if (!SvROK(ST(3)) || SvTYPE(SvRV(ST(3))) != SVt_PVAV)
+ croak("i_matrix_transform: parameter 4 must be an array ref\n");
+ av=(AV*)SvRV(ST(3));
+ len=av_len(av)+1;
+ if (len > 9)
+ len = 9;
+ for (i = 0; i < len; ++i) {
+ sv1=(*(av_fetch(av,i,0)));
+ matrix[i] = SvNV(sv1);
+ }
+ for (; i < 9; ++i)
+ matrix[i] = 0;
+ RETVAL = i_matrix_transform(im, xsize, ysize, matrix);
+ OUTPUT:
+ RETVAL
void
i_gaussian(im,stdev)
PUSHs(newRV_noinc((SV*)ct));
}
+void
+i_readgif_multi(fd)
+ int fd
+ PREINIT:
+ i_img **imgs;
+ int count;
+ int i;
+ PPCODE:
+ imgs = i_readgif_multi(fd, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
+void
+i_readgif_multi_scalar(data)
+ PREINIT:
+ i_img **imgs;
+ int count;
+ char *data;
+ unsigned int length;
+ int i;
+ PPCODE:
+ data = (char *)SvPV(ST(0), length);
+ imgs = i_readgif_multi_scalar(data, length, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
-
-
+void
+i_readgif_multi_callback(cb)
+ PREINIT:
+ i_reader_data rd;
+ i_img **imgs;
+ int count;
+ int i;
+ PPCODE:
+ rd.sv = ST(0);
+ imgs = i_readgif_multi_callback(read_callback, (char *)&rd, &count);
+ if (imgs) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
+ PUSHs(sv);
+ }
+ myfree(imgs);
+ }
#endif
PREINIT:
i_errmsg *errors;
int i;
- int count;
AV *av;
SV *ref;
SV *sv;
# this is mostly for testing...
-Imager::Color
+SV *
i_get_pixel(im, x, y)
Imager::ImgRaw im
int x
int y;
+ PREINIT:
+ i_color *color;
+ CODE:
+ color = (i_color *)mymalloc(sizeof(i_color));
+ if (i_gpix(im, x, y, color) == 0) {
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), "Imager::Color", (void *)color);
+ }
+ else {
+ myfree(color);
+ ST(0) = &PL_sv_undef;
+ }
+
+
+int
+i_ppix(im, x, y, cl)
+ Imager::ImgRaw im
+ int x
+ int y
+ Imager::Color cl
+
+Imager::ImgRaw
+i_img_pal_new(x, y, channels, maxpal)
+ int x
+ int y
+ int channels
+ int maxpal
+
+Imager::ImgRaw
+i_img_to_pal(src, quant)
+ Imager::ImgRaw src
+ PREINIT:
+ HV *hv;
+ i_quantize quant;
+ CODE:
+ if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
+ croak("i_img_to_pal: second argument must be a hash ref");
+ hv = (HV *)SvRV(ST(1));
+ memset(&quant, 0, sizeof(quant));
+ quant.mc_size = 256;
+ quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
+ handle_quant_opts(&quant, hv);
+ RETVAL = i_img_to_pal(src, &quant);
+ if (RETVAL) {
+ copy_colors_back(hv, &quant);
+ }
+ myfree(quant.mc_colors);
+ OUTPUT:
+ RETVAL
+
+Imager::ImgRaw
+i_img_to_rgb(src)
+ Imager::ImgRaw src
+
+void
+i_gpal(im, l, r, y)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ PREINIT:
+ i_palidx *work;
+ int count, i;
+ PPCODE:
+ if (l < r) {
+ work = mymalloc((r-l) * sizeof(i_palidx));
+ count = i_gpal(im, l, r, y, 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 * sizeof(i_palidx))));
+ }
+ myfree(work);
+ }
+ else {
+ if (GIMME_V != G_ARRAY) {
+ EXTEND(SP, 1);
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+int
+i_ppal(im, l, y, ...)
+ Imager::ImgRaw im
+ int l
+ int y
+ PREINIT:
+ i_palidx *work;
+ int count, i;
+ CODE:
+ if (items > 3) {
+ work = mymalloc(sizeof(i_palidx) * (items-3));
+ for (i=0; i < items-3; ++i) {
+ work[i] = SvIV(ST(i+3));
+ }
+ RETVAL = i_ppal(im, l, l+items-3, y, work);
+ myfree(work);
+ }
+ else {
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+SV *
+i_addcolors(im, ...)
+ Imager::ImgRaw im
+ PREINIT:
+ int index;
+ i_color *colors;
+ int i;
+ CODE:
+ if (items < 2)
+ croak("i_addcolors: no colors to add");
+ colors = mymalloc((items-1) * sizeof(i_color));
+ for (i=0; i < items-1; ++i) {
+ if (sv_isobject(ST(i+1))
+ && sv_derived_from(ST(i+1), "Imager::Color")) {
+ IV tmp = SvIV((SV *)SvRV(ST(i+1)));
+ colors[i] = *(i_color *)tmp;
+ }
+ else {
+ myfree(colors);
+ croak("i_plin: pixels must be Imager::Color objects");
+ }
+ }
+ index = i_addcolors(im, colors, items-1);
+ myfree(colors);
+ if (index == 0) {
+ ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+ }
+ else if (index == -1) {
+ ST(0) = &PL_sv_undef;
+ }
+ else {
+ ST(0) = sv_2mortal(newSViv(index));
+ }
+
+int
+i_setcolors(im, index, ...)
+ Imager::ImgRaw im
+ int index
+ PREINIT:
+ i_color *colors;
+ int i;
+ CODE:
+ if (items < 3)
+ croak("i_setcolors: no colors to add");
+ colors = mymalloc((items-2) * sizeof(i_color));
+ for (i=0; i < items-2; ++i) {
+ if (sv_isobject(ST(i+2))
+ && sv_derived_from(ST(i+2), "Imager::Color")) {
+ IV tmp = SvIV((SV *)SvRV(ST(i+2)));
+ colors[i] = *(i_color *)tmp;
+ }
+ else {
+ myfree(colors);
+ croak("i_setcolors: pixels must be Imager::Color objects");
+ }
+ }
+ RETVAL = i_setcolors(im, index, colors, items-2);
+ myfree(colors);
+
+void
+i_getcolors(im, index, ...)
+ Imager::ImgRaw im
+ int index
+ PREINIT:
+ i_color *colors;
+ int count = 1;
+ int i;
+ PPCODE:
+ if (items > 3)
+ croak("i_getcolors: too many arguments");
+ if (items == 3)
+ count = SvIV(ST(2));
+ if (count < 1)
+ croak("i_getcolors: count must be positive");
+ colors = mymalloc(sizeof(i_color) * count);
+ if (i_getcolors(im, index, colors, count)) {
+ for (i = 0; i < count; ++i) {
+ i_color *pv;
+ SV *sv = sv_newmortal();
+ pv = mymalloc(sizeof(i_color));
+ *pv = colors[i];
+ sv_setref_pv(sv, "Imager::Color", (void *)pv);
+ PUSHs(sv);
+ }
+ }
+ myfree(colors);
+
+
+SV *
+i_colorcount(im)
+ Imager::ImgRaw im
+ PREINIT:
+ int count;
+ CODE:
+ count = i_colorcount(im);
+ if (count >= 0) {
+ ST(0) = sv_2mortal(newSViv(count));
+ }
+ else {
+ ST(0) = &PL_sv_undef;
+ }
+
+SV *
+i_maxcolors(im)
+ Imager::ImgRaw im
+ PREINIT:
+ int count;
+ CODE:
+ count = i_maxcolors(im);
+ if (count >= 0) {
+ ST(0) = sv_2mortal(newSViv(count));
+ }
+ else {
+ ST(0) = &PL_sv_undef;
+ }
+
+SV *
+i_findcolor(im, color)
+ Imager::ImgRaw im
+ Imager::Color color
+ PREINIT:
+ i_palidx index;
+ CODE:
+ if (i_findcolor(im, color, &index)) {
+ ST(0) = sv_2mortal(newSViv(index));
+ }
+ else {
+ ST(0) = &PL_sv_undef;
+ }
+
+int
+i_img_bits(im)
+ Imager::ImgRaw im
+
+int
+i_img_type(im)
+ Imager::ImgRaw im
+
+int
+i_img_virtual(im)
+ Imager::ImgRaw im
+
+void
+i_gsamp(im, l, r, y, ...)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ PREINIT:
+ int *chans;
+ int chan_count;
+ i_sample_t *data;
+ int count, i;
+ PPCODE:
+ if (items < 5)
+ croak("No channel numbers supplied to g_samp()");
+ if (l < r) {
+ chan_count = items - 4;
+ chans = mymalloc(sizeof(int) * chan_count);
+ for (i = 0; i < chan_count; ++i)
+ chans[i] = SvIV(ST(i+4));
+ data = mymalloc(sizeof(i_sample_t) * (r-l) * chan_count);
+ count = i_gsamp(im, l, r, y, data, chans, chan_count);
+ if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i)
+ PUSHs(sv_2mortal(newSViv(data[i])));
+ }
+ else {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv(data, count * sizeof(i_sample_t))));
+ }
+ }
+ else {
+ if (GIMME_V != G_ARRAY) {
+ EXTEND(SP, 1);
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+Imager::ImgRaw
+i_img_masked_new(targ, mask, x, y, w, h)
+ Imager::ImgRaw targ
+ int x
+ int y
+ int w
+ int h
+ PREINIT:
+ i_img *mask;
+ CODE:
+ if (SvOK(ST(1))) {
+ if (!sv_isobject(ST(1))
+ || !sv_derived_from(ST(1), "Imager::ImgRaw")) {
+ croak("i_img_masked_new: parameter 2 must undef or an image");
+ }
+ mask = (i_img *)SvIV((SV *)SvRV(ST(1)));
+ }
+ else
+ mask = NULL;
+ RETVAL = i_img_masked_new(targ, mask, x, y, w, h);
+ OUTPUT:
+ RETVAL
+
+int
+i_plin(im, l, y, ...)
+ Imager::ImgRaw im
+ int l
+ int y
+ PREINIT:
+ i_color *work;
+ int count, i;
+ CODE:
+ if (items > 3) {
+ work = mymalloc(sizeof(i_color) * (items-3));
+ for (i=0; i < items-3; ++i) {
+ if (sv_isobject(ST(i+3))
+ && sv_derived_from(ST(i+3), "Imager::Color")) {
+ IV tmp = SvIV((SV *)SvRV(ST(i+3)));
+ work[i] = *(i_color *)tmp;
+ }
+ else {
+ myfree(work);
+ croak("i_plin: pixels must be Imager::Color objects");
+ }
+ }
+ /**(char *)0 = 1;*/
+ RETVAL = i_plin(im, l, l+items-3, y, work);
+ myfree(work);
+ }
+ else {
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+int
+i_ppixf(im, x, y, cl)
+ Imager::ImgRaw im
+ int x
+ int y
+ Imager::Color::Float cl
+
+void
+i_gsampf(im, l, r, y, ...)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ PREINIT:
+ int *chans;
+ int chan_count;
+ i_fsample_t *data;
+ int count, i;
+ PPCODE:
+ if (items < 5)
+ croak("No channel numbers supplied to g_sampf()");
+ if (l < r) {
+ chan_count = items - 4;
+ chans = mymalloc(sizeof(int) * chan_count);
+ for (i = 0; i < chan_count; ++i)
+ chans[i] = SvIV(ST(i+4));
+ data = mymalloc(sizeof(i_fsample_t) * (r-l) * chan_count);
+ count = i_gsampf(im, l, r, y, data, chans, chan_count);
+ if (GIMME_V == G_ARRAY) {
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i)
+ PUSHs(sv_2mortal(newSVnv(data[i])));
+ }
+ else {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv((void *)data, count * sizeof(i_fsample_t))));
+ }
+ }
+ else {
+ if (GIMME_V != G_ARRAY) {
+ EXTEND(SP, 1);
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+int
+i_plinf(im, l, y, ...)
+ Imager::ImgRaw im
+ int l
+ int y
+ PREINIT:
+ i_fcolor *work;
+ int count, i;
+ CODE:
+ if (items > 3) {
+ work = mymalloc(sizeof(i_fcolor) * (items-3));
+ for (i=0; i < items-3; ++i) {
+ if (sv_isobject(ST(i+3))
+ && sv_derived_from(ST(i+3), "Imager::Color::Float")) {
+ IV tmp = SvIV((SV *)SvRV(ST(i+3)));
+ work[i] = *(i_fcolor *)tmp;
+ }
+ else {
+ myfree(work);
+ croak("i_plin: pixels must be Imager::Color::Float objects");
+ }
+ }
+ /**(char *)0 = 1;*/
+ RETVAL = i_plinf(im, l, l+items-3, y, work);
+ myfree(work);
+ }
+ else {
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+
+SV *
+i_gpixf(im, x, y)
+ Imager::ImgRaw im
+ int x
+ int y;
+ PREINIT:
+ i_fcolor *color;
+ CODE:
+ color = (i_fcolor *)mymalloc(sizeof(i_fcolor));
+ if (i_gpixf(im, x, y, color) == 0) {
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), "Imager::Color::Float", (void *)color);
+ }
+ else {
+ myfree(color);
+ ST(0) = &PL_sv_undef;
+ }
+
+void
+i_glin(im, l, r, y)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ PREINIT:
+ i_color *vals;
+ int count, i;
+ PPCODE:
+ if (l < r) {
+ vals = mymalloc((r-l) * sizeof(i_color));
+ count = i_glin(im, l, r, y, vals);
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv;
+ i_color *col = mymalloc(sizeof(i_color));
+ sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::Color", (void *)col);
+ PUSHs(sv);
+ }
+ myfree(vals);
+ }
+
+void
+i_glinf(im, l, r, y)
+ Imager::ImgRaw im
+ int l
+ int r
+ int y
+ PREINIT:
+ i_fcolor *vals;
+ int count, i;
+ PPCODE:
+ if (l < r) {
+ vals = mymalloc((r-l) * sizeof(i_fcolor));
+ count = i_glinf(im, l, r, y, vals);
+ EXTEND(SP, count);
+ for (i = 0; i < count; ++i) {
+ SV *sv;
+ i_fcolor *col = mymalloc(sizeof(i_fcolor));
+ *col = vals[i];
+ sv = sv_newmortal();
+ sv_setref_pv(sv, "Imager::Color::Float", (void *)col);
+ PUSHs(sv);
+ }
+ myfree(vals);
+ }
+
+Imager::ImgRaw
+i_img_16_new(x, y, ch)
+ int x
+ int y
+ int ch
+
+undef_int
+i_tags_addn(im, name, code, idata)
+ Imager::ImgRaw im
+ int code
+ int idata
+ PREINIT:
+ char *name;
+ STRLEN len;
+ CODE:
+ if (SvOK(ST(1)))
+ name = SvPV(ST(1), len);
+ else
+ name = NULL;
+ RETVAL = i_tags_addn(&im->tags, name, code, idata);
+ OUTPUT:
+ RETVAL
+
+undef_int
+i_tags_add(im, name, code, data, idata)
+ Imager::ImgRaw im
+ int code
+ int idata
+ PREINIT:
+ char *name;
+ char *data;
+ STRLEN len;
+ CODE:
+ if (SvOK(ST(1)))
+ name = SvPV(ST(1), len);
+ else
+ name = NULL;
+ if (SvOK(ST(3)))
+ data = SvPV(ST(3), len);
+ else {
+ data = NULL;
+ len = 0;
+ }
+ RETVAL = i_tags_add(&im->tags, name, code, data, len, idata);
+ OUTPUT:
+ RETVAL
+
+SV *
+i_tags_find(im, name, start)
+ Imager::ImgRaw im
+ char *name
+ int start
+ PREINIT:
+ int entry;
+ CODE:
+ if (i_tags_find(&im->tags, name, start, &entry)) {
+ if (entry == 0)
+ ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+ else
+ ST(0) = sv_2mortal(newSViv(entry));
+ } else {
+ ST(0) = &PL_sv_undef;
+ }
+
+SV *
+i_tags_findn(im, code, start)
+ Imager::ImgRaw im
+ int code
+ int start
+ PREINIT:
+ int entry;
+ CODE:
+ if (i_tags_findn(&im->tags, code, start, &entry)) {
+ if (entry == 0)
+ ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+ else
+ ST(0) = sv_2mortal(newSViv(entry));
+ }
+ else
+ ST(0) = &PL_sv_undef;
+
+int
+i_tags_delete(im, entry)
+ Imager::ImgRaw im
+ int entry
+ CODE:
+ RETVAL = i_tags_delete(&im->tags, entry);
+ OUTPUT:
+ RETVAL
+
+int
+i_tags_delbyname(im, name)
+ Imager::ImgRaw im
+ char * name
+ CODE:
+ RETVAL = i_tags_delbyname(&im->tags, name);
+ OUTPUT:
+ RETVAL
+
+int
+i_tags_delbycode(im, code)
+ Imager::ImgRaw im
+ int code
+ CODE:
+ RETVAL = i_tags_delbycode(&im->tags, code);
+ OUTPUT:
+ RETVAL
+
+void
+i_tags_get(im, index)
+ Imager::ImgRaw im
+ int index
+ PPCODE:
+ if (index >= 0 && index < im->tags.count) {
+ i_img_tag *entry = im->tags.tags + index;
+ EXTEND(SP, 5);
+
+ if (entry->name) {
+ PUSHs(sv_2mortal(newSVpv(entry->name, 0)));
+ }
+ else {
+ PUSHs(sv_2mortal(newSViv(entry->code)));
+ }
+ if (entry->data) {
+ PUSHs(sv_2mortal(newSVpvn(entry->data, entry->size)));
+ }
+ else {
+ PUSHs(sv_2mortal(newSViv(entry->idata)));
+ }
+ }
+
+int
+i_tags_count(im)
+ Imager::ImgRaw im
+ CODE:
+ RETVAL = im->tags.count;
+ OUTPUT:
+ RETVAL
+
+#ifdef HAVE_WIN32
+
+void
+i_wf_bbox(face, size, text)
+ char *face
+ int size
+ char *text
+ PREINIT:
+ int cords[6];
+ PPCODE:
+ if (i_wf_bbox(face, size, text, strlen(text), cords)) {
+ EXTEND(SP, 6);
+ PUSHs(sv_2mortal(newSViv(cords[0])));
+ PUSHs(sv_2mortal(newSViv(cords[1])));
+ PUSHs(sv_2mortal(newSViv(cords[2])));
+ PUSHs(sv_2mortal(newSViv(cords[3])));
+ PUSHs(sv_2mortal(newSViv(cords[4])));
+ PUSHs(sv_2mortal(newSViv(cords[5])));
+ }
+
+undef_int
+i_wf_text(face, im, tx, ty, cl, size, text, align, aa)
+ char *face
+ Imager::ImgRaw im
+ int tx
+ int ty
+ Imager::Color cl
+ int size
+ char *text
+ int align
+ int aa
+ CODE:
+ RETVAL = i_wf_text(face, im, tx, ty, cl, size, text, strlen(text),
+ align, aa);
+ OUTPUT:
+ RETVAL
+
+undef_int
+i_wf_cp(face, im, tx, ty, channel, size, text, align, aa)
+ char *face
+ Imager::ImgRaw im
+ int tx
+ int ty
+ int channel
+ int size
+ char *text
+ int align
+ int aa
CODE:
- RETVAL = (i_color *)mymalloc(sizeof(i_color));
- i_gpix(im, x, y, RETVAL);
+ RETVAL = i_wf_cp(face, im, tx, ty, channel, size, text, strlen(text),
+ align, aa);
OUTPUT:
RETVAL
+
+#endif
+
+#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
+
+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)
+ Imager::Font::FT2 font
+ double cheight
+ double cwidth
+ char *text
+ PREINIT:
+ int bbox[6];
+ int i;
+ PPCODE:
+ if (i_ft2_bbox(font, cheight, cwidth, text, strlen(text), bbox)) {
+ EXTEND(SP, 6);
+ for (i = 0; i < 6; ++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, align, aa, vlayout, utf8)
+ Imager::Font::FT2 font
+ Imager::ImgRaw im
+ int tx
+ int ty
+ int channel
+ double cheight
+ double cwidth
+ char *text
+ int align
+ int aa
+ int vlayout
+ int utf8
+ CODE:
+#ifdef SvUTF8
+ if (SvUTF8(ST(7)))
+ utf8 = 1;
+#endif
+ RETVAL = i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text,
+ strlen(text), 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])));
+
+#endif
+
ppport.h
image.c
image.h
+imagei.h
datatypes.h
datatypes.c
feat.h
+img16.c
io.c
io.h
log.c
filters.c
feat.c
font.c
+freetyp2.c Implements freetype2 font support
+maskimg.c
+palimg.c
regmach.c
regmach.h
+rotate.c
stackmach.c
stackmach.h
+tags.c
trans2.c
iolayer.h
iolayer.c
fontfiles/dcr10.afm
fontfiles/dcr10.pfb
fontfiles/dodge.ttf
-lib/Imager/regmach.pod
-lib/Imager/Regops.pm
+lib/Imager/Color.pm
+lib/Imager/Color/Float.pm
lib/Imager/Expr.pm
lib/Imager/Expr/Assem.pm
-lib/Imager/Color.pm
lib/Imager/Font.pm
+lib/Imager/Font/Type1.pm
+lib/Imager/Font/Truetype.pm
+lib/Imager/Font/FreeType2.pm
+lib/Imager/interface.pod
+lib/Imager/Matrix2d.pm
+lib/Imager/regmach.pod
+lib/Imager/Regops.pm
lib/Imager/Transform.pm
t/t00basic.t
+t/t01introvert.t
+t/t020masked.t
+t/t021sixteen.t
t/t05error.t
t/t101jpeg.t
t/t102png.t
t/t30t1font.t
t/t35ttfont.t
t/t36oofont.t
+t/t37w32font.t Tests Win32 GDI font support
+t/t38ft2font.t Tests freetype2 support
t/t40scale.t
t/t50basicoo.t
t/t55trans.t
t/t60dyntest.t
t/t65crop.t
t/t66paste.t
+t/t69rubthru.t
t/t70newgif.t
t/t75polyaa.t
t/t90cc.t
testimg/scale.ppm
testimg/scalei.gif
testimg/screen2.gif
+testimg/screen3.gif
+testimg/test_gimp_pal
testimg/trimgdesc.gif
testimg/trmiddesc.gif
typemap
dynfilt/flines.c
dynfilt/compile.txt
dynfilt/Makefile.PL
+win32.c Implements font support through Win32 GDI
gaussian.o conv.o pnm.o raw.o feat.o font.o
filters.o dynaload.o stackmach.o datatypes.o
regmach.o trans2.o quant.o error.o convert.o
- map.o);
+ map.o tags.o palimg.o maskimg.o img16.o rotate.o);
%opts=(
'NAME' => 'Imager',
sub MY::postamble {
'
-dyntest.(MYEXTLIB) : dynfilt/Makefile
+dyntest.$(MYEXTLIB) : dynfilt/Makefile
cd dynfilt && $(MAKE) $(PASTHRU)
lib/Imager/Regops.pm : regmach.h regops.perl
sub init {
@definc{'/usr/include'}=();
- @incs=(qw(/usr/include /usr/local/include /usr/include/freetype /usr/local/include/freetype), split /:/, $INCPATH );
+ @incs=(qw(/usr/include /usr/local/include /usr/include/freetype /usr/local/include/freetype /usr/include/freetype2 /usr/local/include/freetype2), split /:/, $INCPATH );
@libs=(split(/ /, $Config{'libpth'}), split(/:/, $LIBPATH) );
if ($^O =~ /win32/i && $Config{cc} =~ /\bcl\b/i) {
push(@incs, split /;/, $ENV{INCLUDE}) if exists $ENV{INCLUDE};
push(@libs, split /;/, $ENV{LIB}) if exists $ENV{LIB};
}
+ if ($^O eq 'cygwin') {
+ push(@libs, '/usr/lib/w32api') if -d '/usr/lib/w32api';
+ push(@incs, '/usr/include/w32api') if -d '/usr/lib/w32api';
+ }
$formats{'jpeg'}={
order=>'21',
order=>'30',
def=>'HAVE_LIBT1',
inccheck=>sub { $_[0] eq 't1lib.h' },
- libcheck=>sub { $_[0] eq 'libt1.a' or $_[0] eq "libt1.$lext" },
+ libcheck=>sub { $_[0] eq "libt1$aext" or $_[0] eq "libt1.$lext" },
libfiles=>'-lt1',
objfiles=>'',
docs=>q{
order=>'31',
def=>'HAVE_LIBTT',
inccheck=>sub { $_[0] eq 'freetype.h' },
- libcheck=>sub { $_[0] eq 'libttf.a' or $_[0] eq "libttf.$lext" },
+ libcheck=>sub { $_[0] eq "libttf$aext" or $_[0] eq "libttf.$lext" },
libfiles=>'-lttf',
objfiles=>'',
docs=>q{
used to rasterize for us. The only drawback is that there
are alot of badly designed fonts out there.}
};
+ $formats{'w32'} = {
+ order=>40,
+ def=>'HAVE_WIN32',
+ inccheck=>sub { lc $_[0] eq 'windows.h' },
+ libcheck=>sub { lc $_[0] eq 'gdi32.lib'
+ || lc $_[0] eq 'libgdi32.a' },
+ libfiles=>$^O eq 'cygwin' ? '-lgdi32' : '',
+ objfiles=>'win32.o',
+ docs => <<DOCS
+Uses the Win32 GDI for rendering text.
+
+This currently only works on under normal Win32 and cygwin.
+DOCS
+ };
+ $formats{'freetype2'} = {
+ order=>'29',
+ def=>'HAVE_FT2',
+ inccheck=>sub { lc $_[0] eq '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.
+DOCS
+ };
# Make fix indent
for (keys %formats) { $formats{$_}->{docs} =~ s/^\s+/ /mg; }
}
=================
Imager can be installed on Win32 systems. This was ported and tested
-with Microsoft Visual C++ 6.0 with build 623 of ActivePerl. If you
-have the appropriate libraries installed you can read and write PNG,
-TIFF, PPM and JPEG files. There is currently no support for fonts
-under Win32, though it might be preferable to try to use Win32's
-native font support over the external librarie - why force the user to
-install yet another library?
-
-I haven't tried to target compilers other than VC++, since I don't
-have them installed.
+with Microsoft Visual C++ 6.0 with build 623 of ActivePerl. You can
+use all of the features of Imager. You can also use Win32 GDI fonts
+directly by supplying the 'face' parameter to Imager::Font->new(...).
+
+I've tested with both MSVC++ 6.0 and cygwin (perl 5.6.1).
If you have any problems with the Win32 support, please email
tony@develop-help.com (don't forget to use nmake instead of make).
local errors?
- SEE design/represent.txt for proposed new structure and
interface design that takes these factors into account.
+- define common i_* tags for specifying attribute common among images
+ like spatial resolution (implement for other image types, especially
+ TIFF)
New Features:
- Add mng support.
- Finish antialiased filled polygon function.
+- freetype 2 support
+
+- advanced font layout (spacing, kerning, alignment) (sky)
+
+- font synthesis - synthesize a bold or slanted font from a normal font
+ (or even from an existing bold or slanted font)
+- utf8 support for text output
+
+- image rotation, 3 ways of doing rotation:
+ - exact multiple of 90 degrees (the easy case) (done)
+ - rotation by exact angles that results in no scaling, using
+ interpolation to produce a good result (done)
+ - rotation by shearing, which produces makes lengths in the image larger,
+ but could be useful
+
+- read_multi() needs to handle other multi-image types, such as TIFF
+ (probably the most common)
Clean up:
- Make sure everything is doable with the OO interface
Format specific issues:
-- should i_readgif returned colormap be an arrayref of
- Imager::Color objects? Note that this will break
- compatibility with previous releases.
-
-- if gif_delays is a number instead of an arrayref, use that
- number for each frame
-
- provide patches for libgif and libungif that fix their bugs
and give a useful extension interface. Probe for the
installation of the patches in Makefile.PL to let gif.c
cases. Also allow ascii mode. Need to be able to write
pbm images which needs ties to the quantization code.
+- bmp, pcx and targa image formats
Documentation:
- Add to the documentation
- Write a tutorial?
+- sample code and Imager/Samples.pod describing them
+- Imager/Cookbook.pod
+- modify the .pm files to put pod describing a function close to the
+ function
- Write a guide to installing the helper libraries
- Go through the entire project and add comments in pod
so doco.perl can be used to read them.
If im has the wrong number of channels or is the wrong size then
i_convert() will re-create it.
+Now handles images with more than 8-bits/sample.
+
=cut
*/
int
i_convert(i_img *im, i_img *src, float *coeff, int outchan, int inchan)
{
- i_color *vals;
int x, y;
int i, j;
int ilimit;
return 0;
}
- /* first check the output image */
- if (im->channels != outchan || im->xsize != src->xsize
- || im->ysize != src->ysize) {
- i_img_empty_ch(im, src->xsize, src->ysize, outchan);
+ if (im->type == i_direct_type || src->type == i_direct_type) {
+ /* first check the output image */
+ if (im->channels != outchan || im->xsize != src->xsize
+ || im->ysize != src->ysize) {
+ i_img_exorcise(im);
+ i_img_empty_ch(im, src->xsize, src->ysize, outchan);
+ }
+ if (im->bits == i_8_bits && src->bits == i_8_bits) {
+ i_color *vals;
+
+ vals = mymalloc(sizeof(i_color) * src->xsize);
+ for (y = 0; y < src->ysize; ++y) {
+ i_glin(src, 0, src->xsize, y, vals);
+ for (x = 0; x < src->xsize; ++x) {
+ for (j = 0; j < outchan; ++j) {
+ work[j] = 0;
+ for (i = 0; i < ilimit; ++i) {
+ work[j] += coeff[i+inchan*j] * vals[x].channel[i];
+ }
+ if (i < inchan) {
+ work[j] += coeff[i+inchan*j] * 255.9;
+ }
+ }
+ for (j = 0; j < outchan; ++j) {
+ if (work[j] < 0)
+ vals[x].channel[j] = 0;
+ else if (work[j] >= 256)
+ vals[x].channel[j] = 255;
+ else
+ vals[x].channel[j] = work[j];
+ }
+ }
+ i_plin(im, 0, src->xsize, y, vals);
+ }
+ myfree(vals);
+ }
+ else {
+ i_fcolor *vals;
+
+ vals = mymalloc(sizeof(i_fcolor) * src->xsize);
+ for (y = 0; y < src->ysize; ++y) {
+ i_glinf(src, 0, src->xsize, y, vals);
+ for (x = 0; x < src->xsize; ++x) {
+ for (j = 0; j < outchan; ++j) {
+ work[j] = 0;
+ for (i = 0; i < ilimit; ++i) {
+ work[j] += coeff[i+inchan*j] * vals[x].channel[i];
+ }
+ if (i < inchan) {
+ work[j] += coeff[i+inchan*j];
+ }
+ }
+ for (j = 0; j < outchan; ++j) {
+ if (work[j] < 0)
+ vals[x].channel[j] = 0;
+ else if (work[j] >= 1)
+ vals[x].channel[j] = 1;
+ else
+ vals[x].channel[j] = work[j];
+ }
+ }
+ i_plinf(im, 0, src->xsize, y, vals);
+ }
+ myfree(vals);
+ }
}
- vals = mymalloc(sizeof(i_color) * src->xsize);
- for (y = 0; y < src->ysize; ++y) {
- i_glin(src, 0, src->xsize, y, vals);
- for (x = 0; x < src->xsize; ++x) {
+ else {
+ int count;
+ int outcount;
+ int index;
+ i_color *colors;
+ i_palidx *vals;
+
+ if (im->channels != outchan || im->xsize != src->xsize
+ || im->ysize != src->ysize
+ || i_maxcolors(im) < i_colorcount(src)) {
+ i_img_exorcise(im);
+ i_img_pal_new_low(im, src->xsize, src->ysize, outchan,
+ i_maxcolors(src));
+ }
+ /* just translate the color table */
+ count = i_colorcount(src);
+ outcount = i_colorcount(im);
+ colors = mymalloc(count * sizeof(i_color));
+ i_getcolors(src, 0, colors, count);
+ for (index = 0; index < count; ++index) {
for (j = 0; j < outchan; ++j) {
- work[j] = 0;
- for (i = 0; i < ilimit; ++i) {
- work[j] += coeff[i+inchan*j] * vals[x].channel[i];
- }
- if (i < inchan) {
- work[j] += coeff[i+inchan*j] * 255.9;
- }
+ work[j] = 0;
+ for (i = 0; i < ilimit; ++i) {
+ work[j] += coeff[i+inchan*j] * colors[index].channel[i];
+ }
+ if (i < inchan) {
+ work[j] += coeff[i+inchan*j] * 255.9;
+ }
}
for (j = 0; j < outchan; ++j) {
- if (work[j] < 0)
- vals[x].channel[j] = 0;
- else if (work[j] >= 256)
- vals[x].channel[j] = 255;
- else
- vals[x].channel[j] = work[j];
+ if (work[j] < 0)
+ colors[index].channel[j] = 0;
+ else if (work[j] >= 255)
+ colors[index].channel[j] = 255;
+ else
+ colors[index].channel[j] = work[j];
}
}
- i_plin(im, 0, src->xsize, y, vals);
+ if (count < outcount) {
+ i_setcolors(im, 0, colors, count);
+ }
+ else {
+ i_setcolors(im, 0, colors, outcount);
+ i_addcolors(im, colors, count-outcount);
+ }
+ /* and copy the indicies */
+ vals = mymalloc(sizeof(i_palidx) * im->xsize);
+ for (y = 0; y < im->ysize; ++y) {
+ i_gpal(src, 0, im->xsize, y, vals);
+ i_ppal(im, 0, im->xsize, y, vals);
+ }
}
- myfree(vals);
+
return 1;
}
#define MAXCHANNELS 4
-typedef struct { unsigned char gray_color; } gray_color;
-typedef struct { unsigned char r,g,b; } rgb_color;
-typedef struct { unsigned char r,g,b,a; } rgba_color;
-typedef struct { unsigned char c,m,y,k; } cmyk_color;
+/* used for palette indices in some internal code (which might be
+ exposed at some point
+*/
+typedef unsigned char i_palidx;
+
+/* We handle 2 types of sample, this is hopefully the most common, and the
+ smaller of the ones we support */
+typedef unsigned char i_sample_t;
+
+typedef struct { i_sample_t gray_color; } gray_color;
+typedef struct { i_sample_t r,g,b; } rgb_color;
+typedef struct { i_sample_t r,g,b,a; } rgba_color;
+typedef struct { i_sample_t c,m,y,k; } cmyk_color;
typedef int undef_int; /* special value to put in typemaps to retun undef on 0 and 1 on 1 */
rgb_color rgb;
rgba_color rgba;
cmyk_color cmyk;
- unsigned char channel[MAXCHANNELS];
+ i_sample_t channel[MAXCHANNELS];
unsigned int ui;
} i_color;
+/* this is the larger sample type, it should be able to accurately represent
+ any sample size we use */
+typedef double i_fsample_t;
-struct _i_img {
+typedef struct { i_fsample_t gray_color; } i_fgray_color_t;
+typedef struct { i_fsample_t r, g, b; } i_frgb_color_t;
+typedef struct { i_fsample_t r, g, b, a; } i_frgba_color_t;
+typedef struct { i_fsample_t c, m, y, k; } i_fcmyk_color_t;
+
+typedef union {
+ i_fgray_color_t gray;
+ i_frgb_color_t rgb;
+ i_frgba_color_t rgba;
+ i_fcmyk_color_t cmyk;
+ i_fsample_t channel[MAXCHANNELS];
+} i_fcolor;
+
+typedef enum {
+ i_direct_type, /* direct colour, keeps RGB values per pixel */
+ i_palette_type, /* keeps a palette index per pixel */
+} i_img_type_t;
+
+typedef enum {
+ /* bits per sample, not per pixel */
+ /* a paletted image might have one bit per sample */
+ i_8_bits = 8,
+ i_16_bits = 16,
+ i_double_bits = 64
+} i_img_bits_t;
+
+typedef struct {
+ char *name; /* name of a given tag, might be NULL */
+ int code; /* number of a given tag, -1 if it has no meaning */
+ char *data; /* value of a given tag if it's not an int, may be NULL */
+ int size; /* size of the data */
+ int idata; /* value of a given tag if data is NULL */
+} i_img_tag;
+
+typedef struct {
+ int count; /* how many tags have been set */
+ int alloc; /* how many tags have been allocated for */
+ i_img_tag *tags;
+} i_img_tags;
+
+typedef struct i_img_ i_img;
+typedef int (*i_f_ppix_t)(i_img *im, int x, int y, i_color *pix);
+typedef int (*i_f_ppixf_t)(i_img *im, int x, int y, i_fcolor *pix);
+typedef int (*i_f_plin_t)(i_img *im, int x, int r, int y, i_color *vals);
+typedef int (*i_f_plinf_t)(i_img *im, int x, int r, int y, i_fcolor *vals);
+typedef int (*i_f_gpix_t)(i_img *im, int x, int y, i_color *pix);
+typedef int (*i_f_gpixf_t)(i_img *im, int x, int y, i_fcolor *pix);
+typedef int (*i_f_glin_t)(i_img *im, int x, int r, int y, i_color *vals);
+typedef int (*i_f_glinf_t)(i_img *im, int x, int r, int y, i_fcolor *vals);
+
+typedef int (*i_f_gsamp_t)(i_img *im, int x, int r, int y, i_sample_t *samp,
+ int *chans, int chan_count);
+typedef int (*i_f_gsampf_t)(i_img *im, int x, int r, int y, i_fsample_t *samp,
+ int *chan, int chan_count);
+
+typedef int (*i_f_gpal_t)(i_img *im, int x, int r, int y, i_palidx *vals);
+typedef int (*i_f_ppal_t)(i_img *im, int x, int r, int y, i_palidx *vals);
+typedef int (*i_f_addcolors_t)(i_img *im, i_color *colors, int count);
+typedef int (*i_f_getcolors_t)(i_img *im, int i, i_color *, int count);
+typedef int (*i_f_colorcount_t)(i_img *im);
+typedef int (*i_f_maxcolors_t)(i_img *im);
+typedef int (*i_f_findcolor_t)(i_img *im, i_color *color, i_palidx *entry);
+typedef int (*i_f_setcolors_t)(i_img *im, int index, i_color *colors,
+ int count);
+
+typedef void (*i_f_destroy_t)(i_img *im);
+
+struct i_img_ {
int channels;
int xsize,ysize,bytes;
- unsigned char *data;
unsigned int ch_mask;
+ i_img_bits_t bits;
+ i_img_type_t type;
+ int virtual; /* image might not keep any data, must use functions */
+ unsigned char *idata; /* renamed to force inspection of existing code */
+ /* can be NULL if virtual is non-zero */
+ i_img_tags tags;
- int (*i_f_ppix) (struct _i_img *,int,int,i_color *);
- int (*i_f_gpix) (struct _i_img *,int,int,i_color *);
- int (*i_f_plin) (struct _i_img *,int l, int r, int y, i_color *);
- int (*i_f_glin) (struct _i_img *,int l, int r, int y, i_color *);
void *ext_data;
-};
-typedef struct _i_img i_img;
+ /* interface functions */
+ i_f_ppix_t i_f_ppix;
+ i_f_ppixf_t i_f_ppixf;
+ i_f_plin_t i_f_plin;
+ i_f_plinf_t i_f_plinf;
+ i_f_gpix_t i_f_gpix;
+ i_f_gpixf_t i_f_gpixf;
+ i_f_glin_t i_f_glin;
+ i_f_glinf_t i_f_glinf;
+ i_f_gsamp_t i_f_gsamp;
+ i_f_gsampf_t i_f_gsampf;
+
+ /* only valid for type == i_palette_type */
+ i_f_gpal_t i_f_gpal;
+ i_f_ppal_t i_f_ppal;
+ i_f_addcolors_t i_f_addcolors;
+ i_f_getcolors_t i_f_getcolors;
+ i_f_colorcount_t i_f_colorcount;
+ i_f_maxcolors_t i_f_maxcolors;
+ i_f_findcolor_t i_f_findcolor;
+ i_f_setcolors_t i_f_setcolors;
+
+ i_f_destroy_t i_f_destroy;
+};
-/* used for palette indices in some internal code (which might be
- exposed at some point
-*/
-typedef unsigned char i_palidx;
+/* ext_data for paletted images
+ */
+typedef struct {
+ int count; /* amount of space used in palette (in entries) */
+ int alloc; /* amount of space allocated for palette (in entries) */
+ i_color *pal;
+ int last_found;
+} i_img_pal_ext;
-/* Helper datatypes
+/* Helper datatypes
The types in here so far are:
doubly linked bucket list - pretty efficient
int cnt;
};
-struct octt *octt_new();
+struct octt *octt_new(void);
int octt_add(struct octt *ct,unsigned char r,unsigned char g,unsigned char b);
void octt_dump(struct octt *ct);
void octt_count(struct octt *ct,int *tot,int max,int *overflow);
Varying Bits/Sample
Paletted Images
Performance
+ Robustness
+ XS Changes
=head1 DESCRIPTION
i_palette_type, /* keeps a palette index per pixel */
} i_img_types;
- /* interface functions
+ /* interface functions */
typedef int (*i_f_gpal_t)(i_img *im, int x, int r, int y, i_palidx *vals);
typedef int (*i_f_ppal_t)(i_img *im, int x, int r, int y, i_palidx *vals);
typedef int (*i_f_addcolor_t)(i_img *im, i_color *);
once, though setting the whole palette at once would make existing
image data fairly useless.
+=head1 XS CHANGES
+
+I had been considering moving the i_img object from being an
+Imager::ImgRef object to being an Imager object. But I don't see much
+point to it, so I'll leave it the way it is now.
+
=head1 AUTHOR
Tony Cook <tony@develop-help.com>
16May2001 - initially completed version, could use some polishing
16May2001 - Added i_error stack to the image structure.
+24May2001 - Added XS Changes section (TC)
=cut
}
/*
-=item i_push_error(char const *msg)
+=item i_push_error(int code, char const *msg)
Called by an imager function to push an error message onto the stack.
#endif
#ifdef HAVE_LIBTT
"tt",
+#endif
+#ifdef HAVE_WIN32
+ "w32",
+#endif
+#ifdef HAVE_FT2
+ "ft2",
#endif
"raw",
"pnm",
init_tt();
#endif
+#ifdef HAVE_FT2
+ if (!i_ft2_init())
+ return 0;
+#endif
+
return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */
}
*/
void
-i_close_t1() {
+i_close_t1(void) {
T1_CloseLib();
}
if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
+ if (glyph == NULL)
+ return 0;
mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); }
glyph=T1_AASetString( fontnum, str, len, 0, T1_KERNING, points, NULL);
+ if (glyph == NULL)
+ return 0;
mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent));
mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing));
for ( i = 0; i < len; ++i ) {
j = ustr[i];
if ( i_tt_get_glyph(handle,inst,j) ) {
- width += handle->instanceh[inst].gmetrics[j].advance / 64;
- casc = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
- cdesc = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
+ TT_Glyph_Metrics *gm = handle->instanceh[inst].gmetrics + j;
+ width += gm->advance / 64;
+ casc = gm->bbox.yMax / 64;
+ cdesc = gm->bbox.yMin / 64;
mm_log((1, "i_tt_box_inst: glyph='%c' casc=%d cdesc=%d\n", j, casc, cdesc));
if (first) {
- start = handle->instanceh[inst].gmetrics[j].bbox.xMin / 64;
- ascent = handle->instanceh[inst].gmetrics[j].bbox.yMax / 64;
- descent = handle->instanceh[inst].gmetrics[j].bbox.yMin / 64;
+ start = gm->bbox.xMin / 64;
+ ascent = gm->bbox.yMax / 64;
+ descent = gm->bbox.yMin / 64;
first = 0;
}
+ if (i == len-1) {
+ /* the right-side bearing - in case the right-side of a
+ character goes past the right of the advance width,
+ as is common for italic fonts
+ */
+ int rightb = gm->advance - gm->bearingX
+ - (gm->bbox.xMax - gm->bbox.xMin);
+ /* fprintf(stderr, "font info last: %d %d %d %d\n",
+ gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
+ if (rightb < 0)
+ width -= rightb/64;
+ }
ascent = (ascent > casc ? ascent : casc );
descent = (descent < cdesc ? descent : cdesc);
--- /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[6];
+ if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox)) { 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 "image.h"
+#include <stdio.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+static void ft2_push_message(int code);
+static unsigned long utf8_advance(char **p, int *len);
+
+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;
+ }
+ return 1;
+}
+
+struct FT2_Fonthandle {
+ FT_Face face;
+ int xdpi, ydpi;
+ int hint;
+
+ /* used to adjust so we can align the draw point to the top-left */
+ double matrix[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(char *name, int index) {
+ FT_Error error;
+ FT2_Fonthandle *result;
+ FT_Face face;
+ double matrix[6] = { 1, 0, 0,
+ 0, 1, 0 };
+
+ i_clear_error();
+ error = FT_New_Face(library, name, index, &face);
+ if (error) {
+ ft2_push_message(error);
+ i_push_error(error, "Opening face");
+ return NULL;
+ }
+
+ result = mymalloc(sizeof(FT2_Fonthandle));
+ result->face = face;
+ result->xdpi = result->ydpi = 72;
+
+ /* 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;
+
+ 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, 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, int 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 *text, int len, 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 start = 0;
+
+ 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--) {
+ int c = (unsigned char)*text++;
+
+ index = FT_Get_Char_Index(handle->face, c);
+ error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
+ 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*/
+ int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
+ if (rightb < 0)
+ width -= rightb / 64;
+ }
+ }
+
+ bbox[0] = start;
+ bbox[1] = handle->face->size->metrics.ascender / 64;
+ bbox[2] = width + start;
+ bbox[3] = handle->face->size->metrics.descender / 64;
+ bbox[4] = descent;
+ bbox[5] = ascent;
+
+ return 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;
+ int i;
+
+ 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(min(min(work[0], work[2]),min(work[4], work[6])));
+ bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7])));
+ bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6])));
+ bbox[3] = ceil(max(max(work[1], work[3]),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] = min(bbox[0], bbox2[0]);
+ bbox[1] = min(bbox[1], bbox2[1]);
+ bbox[2] = max(bbox[2], bbox2[2]);
+ bbox[3] = max(bbox[3], bbox2[3]);
+}
+
+/*
+=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int 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 *text, int 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 start = 0;
+ int work[4];
+ int bounds[4];
+ double x = 0, y = 0;
+ int i;
+ FT_GlyphSlot slot;
+ int advx, advy;
+ int loadFlags = FT_LOAD_DEFAULT;
+
+ if (vlayout)
+ loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+
+ 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 = 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, int 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, i_color *cl,
+ double cheight, double cwidth, char *text, int len, int align,
+ int aa, int vlayout, int utf8) {
+ FT_Error error;
+ int index;
+ FT_Glyph_Metrics *gm;
+ int bbox[6];
+ 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 ch;
+ i_color pel;
+ int loadFlags = FT_LOAD_DEFAULT;
+
+ 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))
+ return 0;
+
+ 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 = 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;
+
+ 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)");
+ 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) {
+ for (x = 0; x < slot->bitmap.width; ++x) {
+ int value = map[bmp[x]];
+ if (value) {
+ i_gpix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
+ for (ch = 0; ch < im->channels; ++ch) {
+ pel.channel[ch] =
+ ((255-value)*pel.channel[ch] + value * cl->channel[ch]) / 255;
+ }
+ i_ppix(im, tx+x+slot->bitmap_left, ty+y-slot->bitmap_top, &pel);
+ }
+ }
+ bmp += slot->bitmap.pitch;
+ }
+ }
+
+ tx += slot->advance.x / 64;
+ ty -= slot->advance.y / 64;
+ }
+
+ return 1;
+}
+
+/*
+=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, int 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
+*/
+
+i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
+ double cheight, double cwidth, char *text, int len, int align,
+ int aa, int vlayout, int utf8) {
+ int bbox[8];
+ i_img *work;
+ i_color cl, cl2;
+ int x, y;
+
+ 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);
+ }
+ }
+
+ return 1;
+}
+
+/* uses a method described in fterrors.h to build an error translation
+ function
+*/
+#undef __FT_ERRORS_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;
+}
+
+struct utf8_size {
+ int mask, expect;
+ int size;
+};
+
+struct utf8_size utf8_sizes[] =
+{
+ { 0x80, 0x00, 1 },
+ { 0xE0, 0xC0, 2 },
+ { 0xF0, 0xE0, 3 },
+ { 0xF8, 0xF0, 4 },
+};
+
+/*
+=item utf8_advance(char **p, int *len)
+
+Retreive a UTF8 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.
+
+=cut
+*/
+
+unsigned long utf8_advance(char **p, int *len) {
+ unsigned char c;
+ int i, ci, clen = 0;
+ unsigned char codes[3];
+ if (*len == 0)
+ return ~0UL;
+ c = *(*p)++; --*len;
+
+ for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) {
+ if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) {
+ clen = utf8_sizes[i].size;
+ }
+ }
+ if (clen == 0 || *len < clen-1) {
+ --*p; ++*len;
+ return ~0UL;
+ }
+
+ /* check that each character is well formed */
+ i = 1;
+ ci = 0;
+ while (i < clen) {
+ if (((*p)[ci] & 0xC0) != 0x80) {
+ --*p; ++*len;
+ return ~0UL;
+ }
+ codes[ci] = (*p)[ci];
+ ++ci; ++i;
+ }
+ *p += clen-1; *len -= clen-1;
+ if (c & 0x80) {
+ if ((c & 0xE0) == 0xC0) {
+ return ((c & 0x1F) << 6) + (codes[0] & 0x3F);
+ }
+ else if ((c & 0xF0) == 0xE0) {
+ return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f);
+ }
+ else if ((c & 0xF8) == 0xF0) {
+ return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12)
+ | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F);
+ }
+ else {
+ *p -= clen; *len += clen;
+ return ~0UL;
+ }
+ }
+ else {
+ return c;
+ }
+}
+
+/*
+=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
+*/
+
*/
static char const *gif_error_msg(int code);
-static void gif_push_error();
+static void gif_push_error(void);
#if IM_GIFMAJOR >= 4
+static int gif_read_callback(GifFileType *gft, GifByteType *buf, int length);
+
/*
=item gif_scalar_info
return i_readgif_low(GifFile, colour_table, colours);
}
+/*
+
+Internal function called by i_readgif_multi_low() in error handling
+
+*/
+static void free_images(i_img **imgs, int count) {
+ int i;
+ for (i = 0; i < count; ++i)
+ i_img_destroy(imgs[i]);
+ myfree(imgs);
+}
+
+/*
+=item i_readgif_multi_low(GifFileType *gf, int *count)
+
+Reads one of more gif images from the given GIF file.
+
+Returns a pointer to an array of i_img *, and puts the count into
+*count.
+
+Unlike the normal i_readgif*() functions the images are paletted
+images rather than a combined RGB image.
+
+This functions sets tags on the images returned:
+
+=over
+
+=item gif_left
+
+the offset of the image from the left of the "screen" ("Image Left
+Position")
+
+=item gif_top
+
+the offset of the image from the top of the "screen" ("Image Top Position")
+
+=item gif_interlace
+
+non-zero if the image was interlaced ("Interlace Flag")
+
+=item gif_screen_width
+
+=item gif_screen_height
+
+the size of the logical screen ("Logical Screen Width",
+"Logical Screen Height")
+
+=item gif_local_map
+
+Non-zero if this image had a local color map.
+
+=item gif_background
+
+The index in the global colormap of the logical screen's background
+color. This is only set if the current image uses the global
+colormap.
+
+=item gif_trans_index
+
+The index of the color in the colormap used for transparency. If the
+image has a transparency then it is returned as a 4 channel image with
+the alpha set to zero in this palette entry. ("Transparent Color Index")
+
+=item gif_delay
+
+The delay until the next frame is displayed, in 1/100 of a second.
+("Delay Time").
+
+=item gif_user_input
+
+whether or not a user input is expected before continuing (view dependent)
+("User Input Flag").
+
+=item gif_disposal
+
+how the next frame is displayed ("Disposal Method")
+
+=item gif_loop
+
+the number of loops from the Netscape Loop extension. This may be zero.
+
+=item gif_comment
+
+the first block of the first gif comment before each image.
+
+=back
+
+Where applicable, the ("name") is the name of that field from the GIF89
+standard.
+
+=cut
+*/
+
+i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
+ i_img *img;
+ int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
+ int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
+ ColorMapObject *ColorMap;
+
+ GifRecordType RecordType;
+ GifByteType *Extension;
+
+ GifRowType GifRow;
+ int got_gce = 0;
+ int trans_index; /* transparent index if we see a GCE */
+ int gif_delay; /* delay from a GCE */
+ int user_input; /* user input flag from a GCE */
+ int disposal; /* disposal method from a GCE */
+ int got_ns_loop = 0;
+ int ns_loop;
+ char *comment = NULL; /* a comment */
+ i_img **results = NULL;
+ int result_alloc = 0;
+ int channels;
+
+ *count = 0;
+
+ mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
+
+ BackGround = GifFile->SBackGroundColor;
+
+ Size = GifFile->SWidth * sizeof(GifPixelType);
+
+ if ((GifRow = (GifRowType) mymalloc(Size)) == NULL)
+ m_fatal(0,"Failed to allocate memory required, aborted."); /* First row. */
+
+ /* Scan the content of the GIF file and load the image(s) in: */
+ do {
+ if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Unable to get record type");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+
+ switch (RecordType) {
+ case IMAGE_DESC_RECORD_TYPE:
+ if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Unable to get image descriptor");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+
+ if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
+ mm_log((1, "Adding local colormap\n"));
+ ColorMapSize = ColorMap->ColorCount;
+ } else {
+ /* No colormap and we are about to read in the image -
+ abandon for now */
+ mm_log((1, "Going in with no colormap\n"));
+ i_push_error(0, "Image does not have a local or a global color map");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+
+ Width = GifFile->Image.Width;
+ Height = GifFile->Image.Height;
+ channels = 3;
+ if (got_gce && trans_index >= 0)
+ channels = 4;
+ img = i_img_pal_new(Width, Height, channels, 256);
+ /* populate the palette of the new image */
+ for (i = 0; i < ColorMapSize; ++i) {
+ i_color col;
+ col.rgba.r = ColorMap->Colors[i].Red;
+ col.rgba.g = ColorMap->Colors[i].Green;
+ col.rgba.b = ColorMap->Colors[i].Blue;
+ if (channels == 4 && trans_index == i)
+ col.rgba.a = 0;
+ else
+ col.rgba.a = 255;
+
+ i_addcolors(img, &col, 1);
+ }
+ ++*count;
+ if (*count > result_alloc) {
+ if (result_alloc == 0) {
+ result_alloc = 5;
+ results = mymalloc(result_alloc * sizeof(i_img *));
+ }
+ else {
+ i_img **newresults;
+ result_alloc *= 2;
+ newresults = myrealloc(results, result_alloc * sizeof(i_img *));
+ }
+ }
+ results[*count-1] = img;
+ i_tags_addn(&img->tags, "gif_left", 0, GifFile->Image.Left);
+ /**(char *)0 = 1;*/
+ i_tags_addn(&img->tags, "gif_top", 0, GifFile->Image.Top);
+ i_tags_addn(&img->tags, "gif_interlace", 0, GifFile->Image.Interlace);
+ i_tags_addn(&img->tags, "gif_screen_width", 0, GifFile->SWidth);
+ i_tags_addn(&img->tags, "gif_screen_height", 0, GifFile->SHeight);
+ if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
+ i_tags_addn(&img->tags, "gif_background", 0,
+ GifFile->SBackGroundColor);
+ }
+ if (GifFile->Image.ColorMap) {
+ i_tags_addn(&img->tags, "gif_localmap", 0, 1);
+ }
+
+ if (got_gce) {
+ if (trans_index >= 0)
+ i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
+ i_tags_addn(&img->tags, "gif_delay", 0, gif_delay);
+ i_tags_addn(&img->tags, "gif_user_input", 0, user_input);
+ i_tags_addn(&img->tags, "gif_disposal", 0, disposal);
+ }
+ got_gce = 0;
+ if (got_ns_loop)
+ i_tags_addn(&img->tags, "gif_loop", 0, ns_loop);
+ if (comment) {
+ i_tags_add(&img->tags, "gif_comment", 0, comment, strlen(comment), 0);
+ myfree(comment);
+ comment = NULL;
+ }
+
+ ImageNum++;
+ mm_log((1,"i_readgif_multi: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
+
+ if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
+ GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
+ i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return(0);
+ }
+ if (GifFile->Image.Interlace) {
+ for (Count = i = 0; i < 4; i++) {
+ for (j = InterlacedOffset[i]; j < Height;
+ j += InterlacedJumps[i]) {
+ Count++;
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading GIF line");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+
+ i_ppal(img, 0, Width, j, GifRow);
+ }
+ }
+ }
+ else {
+ for (i = 0; i < Height; i++) {
+ if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading GIF line");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+
+ i_ppal(img, 0, Width, i, GifRow);
+ }
+ }
+ break;
+ case EXTENSION_RECORD_TYPE:
+ /* Skip any extension blocks in file: */
+ if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Reading extension record");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ if (ExtCode == 0xF9) {
+ got_gce = 1;
+ if (Extension[1] & 1)
+ trans_index = Extension[4];
+ else
+ trans_index = -1;
+ gif_delay = Extension[2] + 256 * Extension[3];
+ user_input = (Extension[0] & 2) != 0;
+ disposal = (Extension[0] >> 2) & 3;
+ }
+ if (ExtCode == 0xFF && *Extension == 11) {
+ if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
+ if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "reading loop extension");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ if (Extension && *Extension == 3) {
+ got_ns_loop = 1;
+ ns_loop = Extension[2] + 256 * Extension[3];
+ }
+ }
+ }
+ else if (ExtCode == 0xFE) {
+ /* while it's possible for a GIF file to contain more than one
+ comment, I'm only implementing a single comment per image,
+ with the comment saved into the following image.
+ If someone wants more than that they can implement it.
+ I also don't handle comments that take more than one block.
+ */
+ if (!comment) {
+ comment = mymalloc(*Extension+1);
+ memcpy(comment, Extension+1, *Extension);
+ comment[*Extension] = '\0';
+ }
+ }
+ while (Extension != NULL) {
+ if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "reading next block of extension");
+ free_images(results, *count);
+ DGifCloseFile(GifFile);
+ return NULL;
+ }
+ }
+ break;
+ case TERMINATE_RECORD_TYPE:
+ break;
+ default: /* Should be trapped by DGifGetRecordType. */
+ break;
+ }
+ } while (RecordType != TERMINATE_RECORD_TYPE);
+
+ if (comment) {
+ if (*count) {
+ i_tags_add(&(results[*count-1]->tags), "gif_comment", 0, comment,
+ strlen(comment), 0);
+ }
+ myfree(comment);
+ }
+
+ myfree(GifRow);
+
+ if (DGifCloseFile(GifFile) == GIF_ERROR) {
+ gif_push_error();
+ i_push_error(0, "Closing GIF file object");
+ free_images(results, *count);
+ return NULL;
+ }
+
+ return results;
+}
+
+/*
+=item i_readgif_multi(int fd, int *count)
+
+=cut
+*/
+i_img **
+i_readgif_multi(int fd, int *count) {
+ GifFileType *GifFile;
+
+ i_clear_error();
+
+ mm_log((1,"i_readgif_multi(fd %d, &count %p)\n", fd, count));
+
+ if ((GifFile = DGifOpenFileHandle(fd)) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib file object");
+ mm_log((1,"i_readgif: Unable to open file\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count);
+}
+
+/*
+=item i_readgif_multi_scalar(char *data, int length, int *count)
+
+=cut
+*/
+i_img **
+i_readgif_multi_scalar(char *data, int length, int *count) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ struct gif_scalar_info gsi;
+
+ i_clear_error();
+
+ gsi.cpos=0;
+ gsi.length=length;
+ gsi.data=data;
+
+ mm_log((1,"i_readgif_multi_scalar(data %p, length %d, &count %p)\n",
+ data, length, count));
+
+ if ((GifFile = DGifOpen( (void*) &gsi, my_gif_inputfunc )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_multi_scalar: Unable to open scalar datasource.\n"));
+ return NULL;
+ }
+
+ return i_readgif_multi_low(GifFile, count);
+#else
+ return NULL;
+#endif
+}
+
+/*
+=item i_readgif_callback(i_read_callback_t cb, char *userdata, int **colour_table, int *colours)
+
+Read a GIF file into an Imager RGB file, the data of the GIF file is
+retreived by callin the user supplied callback function.
+
+This function is only used with giflib 4 and higher.
+
+=cut
+*/
+
+i_img**
+i_readgif_multi_callback(i_read_callback_t cb, char *userdata, int *count) {
+#if IM_GIFMAJOR >= 4
+ GifFileType *GifFile;
+ i_img **result;
+
+ i_gen_read_data *gci = i_gen_read_data_new(cb, userdata);
+
+ i_clear_error();
+
+ mm_log((1,"i_readgif_multi_callback(callback %p, userdata %p, count %p)\n", cb, userdata, count));
+ if ((GifFile = DGifOpen( (void*) gci, gif_read_callback )) == NULL) {
+ gif_push_error();
+ i_push_error(0, "Cannot create giflib callback object");
+ mm_log((1,"i_readgif_callback: Unable to open callback datasource.\n"));
+ myfree(gci);
+ return NULL;
+ }
+
+ result = i_readgif_multi_low(GifFile, count);
+ free_gen_read_data(gci);
+
+ return result;
+#else
+ return NULL;
+#endif
+}
+
/*
=item i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[])
=cut
*/
-static void gif_push_error() {
+static void gif_push_error(void) {
int code = GifLastError(); /* clears saved error */
i_push_error(code, gif_error_msg(code));
#include "image.h"
+#include "imagei.h"
#include "io.h"
/*
#define minmax(a,b,i) ( ((a>=i)?a: ( (b<=i)?b:i )) )
/* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
-void fake() { ceil(1); }
+void fake(void) { ceil(1); }
+
+static int i_ppix_d(i_img *im, int x, int y, i_color *val);
+static int i_gpix_d(i_img *im, int x, int y, i_color *val);
+static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals);
+static int i_plin_d(i_img *im, int l, int r, int y, i_color *vals);
+static int i_ppixf_d(i_img *im, int x, int y, i_fcolor *val);
+static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val);
+static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_plinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
+static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);
/*
=item ICL_new_internal(r, g, b, a)
myfree(cl);
}
+/*
+=item i_fcolor_new(double r, double g, double b, double a)
+
+=cut
+*/
+i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
+ i_fcolor *cl = NULL;
+
+ mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
+
+ if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) m_fatal(2,"malloc() error\n");
+ cl->rgba.r = r;
+ cl->rgba.g = g;
+ cl->rgba.b = b;
+ cl->rgba.a = a;
+ mm_log((1,"(%p) <- i_fcolor_new\n",cl));
+
+ return cl;
+}
+
+/*
+=item i_fcolor_destroy(i_fcolor *cl)
+
+=cut
+*/
+void i_fcolor_destroy(i_fcolor *cl) {
+ myfree(cl);
+}
+
+/*
+=item IIM_base_8bit_direct (static)
+
+A static i_img object used to initialize direct 8-bit per sample images.
+
+=cut
+*/
+static i_img IIM_base_8bit_direct =
+{
+ 0, /* channels set */
+ 0, 0, 0, /* xsize, ysize, bytes */
+ ~0, /* ch_mask */
+ i_8_bits, /* bits */
+ i_direct_type, /* type */
+ 0, /* virtual */
+ NULL, /* idata */
+ { 0, 0, NULL }, /* tags */
+ NULL, /* ext_data */
+
+ i_ppix_d, /* i_f_ppix */
+ i_ppixf_d, /* i_f_ppixf */
+ i_plin_d, /* i_f_plin */
+ i_plinf_d, /* i_f_plinf */
+ i_gpix_d, /* i_f_gpix */
+ i_gpixf_d, /* i_f_gpixf */
+ i_glin_d, /* i_f_glin */
+ i_glinf_d, /* i_f_glinf */
+ i_gsamp_d, /* i_f_gsamp */
+ i_gsampf_d, /* i_f_gsampf */
+
+ NULL, /* i_f_gpal */
+ NULL, /* i_f_ppal */
+ NULL, /* i_f_addcolors */
+ NULL, /* i_f_getcolors */
+ NULL, /* i_f_colorcount */
+ NULL, /* i_f_maxcolors */
+ NULL, /* i_f_findcolor */
+ NULL, /* i_f_setcolors */
+
+ NULL, /* i_f_destroy */
+};
+
+/*static void set_8bit_direct(i_img *im) {
+ im->i_f_ppix = i_ppix_d;
+ im->i_f_ppixf = i_ppixf_d;
+ im->i_f_plin = i_plin_d;
+ im->i_f_plinf = i_plinf_d;
+ im->i_f_gpix = i_gpix_d;
+ im->i_f_gpixf = i_gpixf_d;
+ im->i_f_glin = i_glin_d;
+ im->i_f_glinf = i_glinf_d;
+ im->i_f_gpal = NULL;
+ im->i_f_ppal = NULL;
+ im->i_f_addcolor = NULL;
+ im->i_f_getcolor = NULL;
+ im->i_f_colorcount = NULL;
+ im->i_f_findcolor = NULL;
+ }*/
+
/*
=item IIM_new(x, y, ch)
void
IIM_DESTROY(i_img *im) {
mm_log((1,"IIM_DESTROY(im* %p)\n",im));
+ i_img_destroy(im);
/* myfree(cl); */
}
if ( (im=mymalloc(sizeof(i_img))) == NULL)
m_fatal(2,"malloc() error\n");
+ *im = IIM_base_8bit_direct;
im->xsize=0;
im->ysize=0;
im->channels=3;
im->ch_mask=MAXINT;
im->bytes=0;
- im->data=NULL;
-
- im->i_f_ppix=i_ppix_d;
- im->i_f_gpix=i_gpix_d;
- im->i_f_plin=i_plin_d;
- im->i_f_glin=i_glin_d;
- im->ext_data=NULL;
+ im->idata=NULL;
mm_log((1,"(%p) <- i_img_struct\n",im));
return im;
x - xsize of destination image
y - ysize of destination image
+**FIXME** what happens if a live image is passed in here?
+
+Should this just call i_img_empty_ch()?
+
=cut
*/
i_img *
i_img_empty(i_img *im,int x,int y) {
mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
- if (im==NULL)
- if ( (im=mymalloc(sizeof(i_img))) == NULL)
- m_fatal(2,"malloc() error\n");
-
- im->xsize = x;
- im->ysize = y;
- im->channels = 3;
- im->ch_mask = MAXINT;
- im->bytes=x*y*im->channels;
- if ( (im->data = mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n");
- memset(im->data, 0, (size_t)im->bytes);
-
- im->i_f_ppix = i_ppix_d;
- im->i_f_gpix = i_gpix_d;
- im->i_f_plin = i_plin_d;
- im->i_f_glin = i_glin_d;
- im->ext_data = NULL;
-
- mm_log((1,"(%p) <- i_img_empty\n", im));
- return im;
+ return i_img_empty_ch(im, x, y, 3);
}
/*
if (im == NULL)
if ( (im=mymalloc(sizeof(i_img))) == NULL)
m_fatal(2,"malloc() error\n");
-
+
+ memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
+ i_tags_new(&im->tags);
im->xsize = x;
im->ysize = y;
im->channels = ch;
im->ch_mask = MAXINT;
im->bytes=x*y*im->channels;
- if ( (im->data=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n");
- memset(im->data,0,(size_t)im->bytes);
+ if ( (im->idata=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n");
+ memset(im->idata,0,(size_t)im->bytes);
- im->i_f_ppix = i_ppix_d;
- im->i_f_gpix = i_gpix_d;
- im->i_f_plin = i_plin_d;
- im->i_f_glin = i_glin_d;
im->ext_data = NULL;
mm_log((1,"(%p) <- i_img_empty_ch\n",im));
void
i_img_exorcise(i_img *im) {
mm_log((1,"i_img_exorcise(im* 0x%x)\n",im));
- if (im->data != NULL) { myfree(im->data); }
- im->data = NULL;
+ i_tags_destroy(&im->tags);
+ if (im->i_f_destroy)
+ (im->i_f_destroy)(im);
+ if (im->idata != NULL) { myfree(im->idata); }
+ im->idata = NULL;
im->xsize = 0;
im->ysize = 0;
im->channels = 0;
mm_log((1,"i_img_info(im 0x%x)\n",im));
if (im != NULL) {
mm_log((1,"i_img_info: xsize=%d ysize=%d channels=%d mask=%ud\n",im->xsize,im->ysize,im->channels,im->ch_mask));
- mm_log((1,"i_img_info: data=0x%d\n",im->data));
+ mm_log((1,"i_img_info: idata=0x%d\n",im->idata));
info[0] = im->xsize;
info[1] = im->ysize;
info[2] = im->channels;
=cut
*/
int
-i_ppix(i_img *im, int x, int y, i_color *val) { return im->i_f_ppix(im, x, y, val); }
+(i_ppix)(i_img *im, int x, int y, i_color *val) { return im->i_f_ppix(im, x, y, val); }
/*
=item i_gpix(im, x, y, &col)
=cut
*/
int
-i_gpix(i_img *im, int x, int y, i_color *val) { return im->i_f_gpix(im, x, y, val); }
-
-/*
-=item i_ppix_d(im, x, y, col)
-
-Internal function.
-
-This is the function kept in the i_f_ppix member of an i_img object.
-It does a normal store of a pixel into the image with range checking.
-
-Returns true if the pixel could be set, false otherwise.
-
-=cut
-*/
-int
-i_ppix_d(i_img *im, int x, int y, i_color *val) {
- int ch;
-
- if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
- for(ch=0;ch<im->channels;ch++)
- if (im->ch_mask&(1<<ch))
- im->data[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
- return 0;
- }
- return -1; /* error was clipped */
-}
-
-/*
-=item i_gpix_d(im, x, y, &col)
-
-Internal function.
-
-This is the function kept in the i_f_gpix member of an i_img object.
-It does normal retrieval of a pixel from the image with range checking.
-
-Returns true if the pixel could be set, false otherwise.
-
-=cut
-*/
-int
-i_gpix_d(i_img *im, int x, int y, i_color *val) {
- int ch;
- if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
- for(ch=0;ch<im->channels;ch++)
- val->channel[ch]=im->data[(x+y*im->xsize)*im->channels+ch];
- return 0;
- }
- return -1; /* error was cliped */
-}
-
-/*
-=item i_glin_d(im, l, r, y, vals)
-
-Reads a line of data from the image, storing the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at space for (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut */
-int
-i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
- int ch, count, i;
- unsigned char *data;
- if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
- if (r > im->xsize)
- r = im->xsize;
- data = im->data + (l+y*im->xsize) * im->channels;
- count = r - l;
- for (i = 0; i < count; ++i) {
- for (ch = 0; ch < im->channels; ++ch)
- vals[i].channel[ch] = *data++;
- }
- return count;
- }
- else {
- return 0;
- }
-}
-/*
-=item i_plin_d(im, l, r, y, vals)
-
-Writes a line of data into the image, using the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut */
-int
-i_plin_d(i_img *im, int l, int r, int y, i_color *vals) {
- int ch, count, i;
- unsigned char *data;
- if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
- if (r > im->xsize)
- r = im->xsize;
- data = im->data + (l+y*im->xsize) * im->channels;
- count = r - l;
- for (i = 0; i < count; ++i) {
- for (ch = 0; ch < im->channels; ++ch) {
- if (im->ch_mask & (1 << ch))
- *data = vals[i].channel[ch];
- ++data;
- }
- }
- return count;
- }
- else {
- return 0;
- }
-}
+(i_gpix)(i_img *im, int x, int y, i_color *val) { return im->i_f_gpix(im, x, y, val); }
/*
=item i_ppix_pch(im, x, y, ch)
*/
float
i_gpix_pch(i_img *im,int x,int y,int ch) {
- if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) return ((float)im->data[(x+y*im->xsize)*im->channels+ch]/255);
+ /* FIXME */
+ if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) return ((float)im->idata[(x+y*im->xsize)*im->channels+ch]/255);
else return 0;
}
void
i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
- i_color pv;
int x, y, t, ttx, tty;
-
+
if (x2<x1) { t=x1; x1=x2; x2=t; }
if (y2<y1) { t=y1; y1=y2; y2=t; }
-
+
mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
im, src, x1, y1, x2, y2, tx, ty));
-
+
+ if (im->bits == i_8_bits) {
+ i_color pv;
tty = ty;
for(y=y1; y<y2; y++) {
- ttx = tx;
- for(x=x1; x<x2; x++) {
- i_gpix(src, x, y, &pv);
- i_ppix(im, ttx, tty, &pv);
- ttx++;
+ ttx = tx;
+ for(x=x1; x<x2; x++) {
+ i_gpix(src, x, y, &pv);
+ i_ppix(im, ttx, tty, &pv);
+ ttx++;
+ }
+ tty++;
+ }
+ }
+ else {
+ i_fcolor pv;
+ tty = ty;
+ for(y=y1; y<y2; y++) {
+ ttx = tx;
+ for(x=x1; x<x2; x++) {
+ i_gpixf(src, x, y, &pv);
+ i_ppixf(im, ttx, tty, &pv);
+ ttx++;
+ }
+ tty++;
}
- tty++;
}
}
void
i_copy(i_img *im, i_img *src) {
- i_color *pv;
int y, y1, x1;
mm_log((1,"i_copy(im* %p,src %p)\n", im, src));
x1 = src->xsize;
y1 = src->ysize;
- i_img_empty_ch(im, x1, y1, src->channels);
- pv = mymalloc(sizeof(i_color) * x1);
-
- for (y = 0; y < y1; ++y) {
- i_glin(src, 0, x1, y, pv);
- i_plin(im, 0, x1, y, pv);
+ if (src->type == i_direct_type) {
+ if (src->bits == i_8_bits) {
+ i_color *pv;
+ i_img_empty_ch(im, x1, y1, src->channels);
+ pv = mymalloc(sizeof(i_color) * x1);
+
+ for (y = 0; y < y1; ++y) {
+ i_glin(src, 0, x1, y, pv);
+ i_plin(im, 0, x1, y, pv);
+ }
+ myfree(pv);
+ }
+ else {
+ /* currently the only other depth is 16 */
+ i_fcolor *pv;
+ i_img_16_new_low(im, x1, y1, src->channels);
+ pv = mymalloc(sizeof(i_fcolor) * x1);
+ for (y = 0; y < y1; ++y) {
+ i_glinf(src, 0, x1, y, pv);
+ i_plinf(im, 0, x1, y, pv);
+ }
+ myfree(pv);
+ }
+ }
+ else {
+ i_color temp;
+ int index;
+ int count;
+ i_palidx *vals;
+
+ /* paletted image */
+ i_img_pal_new_low(im, x1, y1, src->channels, i_maxcolors(src));
+ /* copy across the palette */
+ count = i_colorcount(src);
+ for (index = 0; index < count; ++index) {
+ i_getcolors(src, index, &temp, 1);
+ i_addcolors(im, &temp, 1);
+ }
+
+ vals = mymalloc(sizeof(i_palidx) * x1);
+ for (y = 0; y < y1; ++y) {
+ i_gpal(src, 0, x1, y, vals);
+ i_ppal(im, 0, x1, y, vals);
+ }
+ myfree(vals);
}
- myfree(pv);
}
=cut
*/
-void
+int
i_rubthru(i_img *im,i_img *src,int tx,int ty) {
- i_color pv, orig, dest;
int x, y, ttx, tty;
+ int chancount;
+ int chans[3];
+ int alphachan;
+ int ch;
mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d)\n", im, src, tx, ty));
+ i_clear_error();
- if (im->channels != 3) { fprintf(stderr,"Destination is not in rgb mode.\n"); exit(3); }
- if (src->channels != 4) { fprintf(stderr,"Source is not in rgba mode.\n"); exit(3); }
-
- ttx = tx;
- for(x=0; x<src->xsize; x++) {
- tty=ty;
- for(y=0;y<src->ysize;y++) {
- /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
- i_gpix(src, x, y, &pv);
- i_gpix(im, ttx, tty, &orig);
- dest.rgb.r = (pv.rgba.a*pv.rgba.r+(255-pv.rgba.a)*orig.rgb.r)/255;
- dest.rgb.g = (pv.rgba.a*pv.rgba.g+(255-pv.rgba.a)*orig.rgb.g)/255;
- dest.rgb.b = (pv.rgba.a*pv.rgba.b+(255-pv.rgba.a)*orig.rgb.b)/255;
- i_ppix(im, ttx, tty, &dest);
- tty++;
+ if (im->channels == 3 && src->channels == 4) {
+ chancount = 3;
+ chans[0] = 0; chans[1] = 1; chans[2] = 2;
+ alphachan = 3;
+ }
+ else if (im->channels == 3 && src->channels == 2) {
+ chancount = 3;
+ chans[0] = chans[1] = chans[2] = 0;
+ alphachan = 1;
+ }
+ else if (im->channels == 1 && src->channels == 2) {
+ chancount = 1;
+ chans[0] = 0;
+ alphachan = 1;
+ }
+ else {
+ i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (3,2) or (1,2)");
+ return 0;
+ }
+
+ if (im->bits <= 8) {
+ /* if you change this code, please make sure the else branch is
+ changed in a similar fashion - TC */
+ int alpha;
+ i_color pv, orig, dest;
+ ttx = tx;
+ for(x=0; x<src->xsize; x++) {
+ tty=ty;
+ for(y=0;y<src->ysize;y++) {
+ /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
+ i_gpix(src, x, y, &pv);
+ i_gpix(im, ttx, tty, &orig);
+ alpha = pv.channel[alphachan];
+ for (ch = 0; ch < chancount; ++ch) {
+ dest.channel[ch] = (alpha * pv.channel[chans[ch]]
+ + (255 - alpha) * orig.channel[ch])/255;
+ }
+ i_ppix(im, ttx, tty, &dest);
+ tty++;
+ }
+ ttx++;
+ }
+ }
+ else {
+ double alpha;
+ i_fcolor pv, orig, dest;
+
+ ttx = tx;
+ for(x=0; x<src->xsize; x++) {
+ tty=ty;
+ for(y=0;y<src->ysize;y++) {
+ /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
+ i_gpixf(src, x, y, &pv);
+ i_gpixf(im, ttx, tty, &orig);
+ alpha = pv.channel[alphachan];
+ for (ch = 0; ch < chancount; ++ch) {
+ dest.channel[ch] = alpha * pv.channel[chans[ch]]
+ + (1 - alpha) * orig.channel[ch];
+ }
+ i_ppixf(im, ttx, tty, &dest);
+ tty++;
+ }
+ ttx++;
}
- ttx++;
}
+
+ return 1;
}
return new_img;
}
+/*
+=item i_sametype(i_img *im, int xsize, int ysize)
+
+Returns an image of the same type (sample size, channels, paletted/direct).
+
+For paletted images the palette is copied from the source.
+
+=cut
+*/
+
+i_img *i_sametype(i_img *src, int xsize, int ysize) {
+ if (src->type == i_direct_type) {
+ if (src->bits == 8) {
+ return i_img_empty_ch(NULL, xsize, ysize, src->channels);
+ }
+ else if (src->bits == 16) {
+ return i_img_16_new(xsize, ysize, src->channels);
+ }
+ else {
+ i_push_error(0, "Unknown image bits");
+ return NULL;
+ }
+ }
+ else {
+ i_color col;
+ int i;
+
+ i_img *targ = i_img_pal_new(xsize, ysize, src->channels, i_maxcolors(src));
+ for (i = 0; i < i_colorcount(src); ++i) {
+ i_getcolors(src, i, &col, 1);
+ i_addcolors(targ, &col, 1);
+ }
+
+ return targ;
+ }
+}
/*
=item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
/*
+=back
+
+=head2 8-bit per sample image internal functions
+
+These are the functions installed in an 8-bit per sample image.
+
+=over
+
+=item i_ppix_d(im, x, y, col)
+
+Internal function.
+
+This is the function kept in the i_f_ppix member of an i_img object.
+It does a normal store of a pixel into the image with range checking.
+
+Returns 0 if the pixel could be set, -1 otherwise.
+
+=cut
+*/
+int
+i_ppix_d(i_img *im, int x, int y, i_color *val) {
+ int ch;
+
+ if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
+ for(ch=0;ch<im->channels;ch++)
+ if (im->ch_mask&(1<<ch))
+ im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
+ return 0;
+ }
+ return -1; /* error was clipped */
+}
+
+/*
+=item i_gpix_d(im, x, y, &col)
+
+Internal function.
+
+This is the function kept in the i_f_gpix member of an i_img object.
+It does normal retrieval of a pixel from the image with range checking.
+
+Returns 0 if the pixel could be set, -1 otherwise.
+
+=cut
+*/
+int
+i_gpix_d(i_img *im, int x, int y, i_color *val) {
+ int ch;
+ if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+ for(ch=0;ch<im->channels;ch++)
+ val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
+ return 0;
+ }
+ return -1; /* error was cliped */
+}
+
+/*
+=item i_glin_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+int
+i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ vals[i].channel[ch] = *data++;
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plin_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+int
+i_plin_d(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ *data = vals[i].channel[ch];
+ ++data;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_ppixf_d(im, x, y, val)
+
+=cut
+*/
+int
+i_ppixf_d(i_img *im, int x, int y, i_fcolor *val) {
+ int ch;
+
+ if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
+ for(ch=0;ch<im->channels;ch++)
+ if (im->ch_mask&(1<<ch)) {
+ im->idata[(x+y*im->xsize)*im->channels+ch] =
+ SampleFTo8(val->channel[ch]);
+ }
+ return 0;
+ }
+ return -1; /* error was clipped */
+}
+
+/*
+=item i_gpixf_d(im, x, y, val)
+
+=cut
+*/
+int
+i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
+ int ch;
+ if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
+ for(ch=0;ch<im->channels;ch++) {
+ val->channel[ch] =
+ Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
+ }
+ return 0;
+ }
+ return -1; /* error was cliped */
+}
+
+/*
+=item i_glinf_d(im, l, r, y, vals)
+
+Reads a line of data from the image, storing the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at space for (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+put the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+int
+i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ vals[i].channel[ch] = SampleFTo8(*data++);
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plinf_d(im, l, r, y, vals)
+
+Writes a line of data into the image, using the pixels at vals.
+
+The line runs from (l,y) inclusive to (r,y) non-inclusive
+
+vals should point at (r-l) pixels.
+
+l should never be less than zero (to avoid confusion about where to
+get the pixels in vals).
+
+Returns the number of pixels copied (eg. if r, l or y is out of range)
+
+=cut
+*/
+int
+i_plinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ int ch, count, i;
+ unsigned char *data;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ if (im->ch_mask & (1 << ch))
+ *data = Sample8ToF(vals[i].channel[ch]);
+ ++data;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chans, an array of int with chan_count
+elements.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps,
+ int *chans, int chan_count) {
+ int ch, count, i, w;
+ unsigned char *data;
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[chans[ch]];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ else {
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[ch];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
+
+Reads sample values from im for the horizontal line (l, y) to (r-1,y)
+for the channels specified by chan_mask, where bit 0 is the first
+channel.
+
+Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
+
+=cut
+*/
+int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps,
+ int *chans, int chan_count) {
+ int ch, count, i, w;
+ unsigned char *data;
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ }
+ }
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = im->idata + (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[chans[ch]];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ else {
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = data[ch];
+ ++count;
+ }
+ data += im->channels;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=back
+
+=head2 Image method wrappers
+
+These functions provide i_fsample_t functions in terms of their
+i_sample_t versions.
+
+=over
+
+=item i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+
+=cut
+*/
+
+int i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ for (ch = 0; ch < im->channels; ++ch)
+ temp.channel[ch] = SampleFTo8(pix->channel[ch]);
+
+ return i_ppix(im, x, y, &temp);
+}
+
+/*
+=item i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ if (i_gpix(im, x, y, &temp)) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix->channel[ch] = Sample8ToF(temp.channel[ch]);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/*
+=item i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i, ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ work[i].channel[ch] = SampleFTo8(pix[i].channel[ch]);
+ }
+ ret = i_plin(im, l, r, y, work);
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+
+=cut
+*/
+int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i, ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ ret = i_plin(im, l, r, y, work);
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix[i].channel[ch] = Sample8ToF(work[i].channel[ch]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, int *chans, int chan_count)
+
+=cut
+*/
+int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp,
+ int *chans, int chan_count) {
+ i_sample_t *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ int ret;
+ int i;
+ work = mymalloc(sizeof(i_sample_t) * (r-l));
+ ret = i_gsamp(im, l, r, y, work, chans, chan_count);
+ for (i = 0; i < ret; ++i) {
+ samp[i] = Sample8ToF(work[i]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=back
+
+=head2 Palette wrapper functions
+
+Used for virtual images, these forward palette calls to a wrapped image,
+assuming the wrapped image is the first pointer in the structure that
+im->ext_data points at.
+
+=over
+
+=item i_addcolors_forward(i_img *im, i_color *colors, int count)
+
+=cut
+*/
+int i_addcolors_forward(i_img *im, i_color *colors, int count) {
+ return i_addcolors(*(i_img **)im->ext_data, colors, count);
+}
+
+/*
+=item i_getcolors_forward(i_img *im, int i, i_color *color, int count)
+
+=cut
+*/
+int i_getcolors_forward(i_img *im, int i, i_color *color, int count) {
+ return i_getcolors(*(i_img **)im->ext_data, i, color, count);
+}
+
+/*
+=item i_setcolors_forward(i_img *im, int i, i_color *color, int count)
+
+=cut
+*/
+int i_setcolors_forward(i_img *im, int i, i_color *color, int count) {
+ return i_setcolors(*(i_img **)im->ext_data, i, color, count);
+}
+
+/*
+=item i_colorcount_forward(i_img *im)
+
+=cut
+*/
+int i_colorcount_forward(i_img *im) {
+ return i_colorcount(*(i_img **)im->ext_data);
+}
+
+/*
+=item i_maxcolors_forward(i_img *im)
+
+=cut
+*/
+int i_maxcolors_forward(i_img *im) {
+ return i_maxcolors(*(i_img **)im->ext_data);
+}
+
+/*
+=item i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry)
+
+=cut
+*/
+int i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry) {
+ return i_findcolor(*(i_img **)im->ext_data, color, entry);
+}
+
+/*
+=back
+
+=head2 Stream reading and writing wrapper functions
+
+=over
+
=item i_gen_reader(i_gen_read_data *info, char *buf, int length)
Performs general read buffering for file readers that permit reading
void ICL_DESTROY (i_color *cl);
void ICL_add (i_color *dst, i_color *src, int ch);
+extern i_fcolor *i_fcolor_new(double r, double g, double b, double a);
+extern void i_fcolor_destroy(i_fcolor *cl);
+
i_img *IIM_new(int x,int y,int ch);
void IIM_DESTROY(i_img *im);
i_img *i_img_new( void );
void i_img_info(i_img *im,int *info);
+extern i_img *i_sametype(i_img *im, int xsize, int ysize);
+
+i_img *i_img_pal_new(int x, int y, int ch, int maxpal);
+
/* Image feature settings */
void i_img_setmask (i_img *im,int ch_mask);
int i_ppix(i_img *im,int x,int y,i_color *val);
int i_gpix(i_img *im,int x,int y,i_color *val);
+int i_ppixf(i_img *im,int x,int y,i_color *val);
+int i_gpixf(i_img *im,int x,int y,i_color *val);
+#define i_ppix(im, x, y, val) (((im)->i_f_ppix)((im), (x), (y), (val)))
+#define i_gpix(im, x, y, val) (((im)->i_f_gpix)((im), (x), (y), (val)))
+#define i_ppixf(im, x, y, val) (((im)->i_f_ppixf)((im), (x), (y), (val)))
+#define i_gpixf(im, x, y, val) (((im)->i_f_gpixf)((im), (x), (y), (val)))
+
+#if 0
int i_ppix_d(i_img *im,int x,int y,i_color *val);
int i_gpix_d(i_img *im,int x,int y,i_color *val);
int i_plin_d(i_img *im,int l, int r, int y, i_color *val);
int i_glin_d(i_img *im,int l, int r, int y, i_color *val);
+#endif
#define i_plin(im, l, r, y, val) (((im)->i_f_plin)(im, l, r, y, val))
#define i_glin(im, l, r, y, val) (((im)->i_f_glin)(im, l, r, y, val))
+#define i_plinf(im, l, r, y, val) (((im)->i_f_plinf)(im, l, r, y, val))
+#define i_glinf(im, l, r, y, val) (((im)->i_f_glinf)(im, l, r, y, val))
+
+#define i_gsamp(im, l, r, y, samps, chans, count) \
+ (((im)->i_f_gsamp)((im), (l), (r), (y), (samps), (chans), (count)))
+#define i_gsampf(im, l, r, y, samps, chans, count) \
+ (((im)->i_f_gsampf)((im), (l), (r), (y), (samps), (chans), (count)))
+
+#define i_findcolor(im, color, entry) \
+ (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
+
+#define i_gpal(im, l, r, y, vals) \
+ (((im)->i_f_gpal) ? ((im)->i_f_gpal)((im), (l), (r), (y), (vals)) : 0)
+#define i_ppal(im, l, r, y, vals) \
+ (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0)
+#define i_addcolors(im, colors, count) \
+ (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : 0)
+#define i_getcolors(im, index, color, count) \
+ (((im)->i_f_getcolors) ? \
+ ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0)
+#define i_setcolors(im, index, color, count) \
+ (((im)->i_f_setcolors) ? \
+ ((im)->i_f_setcolors)((im), (index), (color), (count)) : 0)
+#define i_colorcount(im) \
+ (((im)->i_f_colorcount) ? ((im)->i_f_colorcount)(im) : -1)
+#define i_maxcolors(im) \
+ (((im)->i_f_maxcolors) ? ((im)->i_f_maxcolors)(im) : -1)
+#define i_findcolor(im, color, entry) \
+ (((im)->i_f_findcolor) ? ((im)->i_f_findcolor)((im), (color), (entry)) : 0)
+
+#define i_img_virtual(im) ((im)->virtual)
+#define i_img_type(im) ((im)->type)
+#define i_img_bits(im) ((im)->bits)
float i_gpix_pch(i_img *im,int x,int y,int ch);
void i_copyto (i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty);
void i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans);
void i_copy (i_img *im,i_img *src);
-void i_rubthru (i_img *im,i_img *src,int tx,int ty);
+int i_rubthru (i_img *im,i_img *src,int tx,int ty);
undef_int i_flipxy (i_img *im, int direction);
-
-
+extern i_img *i_rotate90(i_img *im, int degrees);
+extern i_img *i_rotate_exact(i_img *im, double amount);
+extern i_img *i_matrix_transform(i_img *im, int xsize, int ysize, double *matrix);
void i_bezier_multi(i_img *im,int l,double *x,double *y,i_color *val);
void i_poly_aa (i_img *im,int l,double *x,double *y,i_color *val);
/* colour manipulation */
extern int i_convert(i_img *im, i_img *src, float *coeff, int outchan, int inchan);
+extern void i_map(i_img *im, unsigned char (*maps)[256], unsigned int mask);
float i_img_diff (i_img *im1,i_img *im2);
#endif /* End of freetype headers */
+#ifdef HAVE_FT2
+
+typedef struct FT2_Fonthandle FT2_Fonthandle;
+extern int i_ft2_init(void);
+extern FT2_Fonthandle * i_ft2_new(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, 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 *text, int len, int *bbox);
+extern int i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
+ i_color *cl, double cheight, double cwidth,
+ char *text, int 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 *text, int len, int align, int aa, int vlayout,
+ int utf8);
+#endif
+#ifdef WIN32
+extern int i_wf_bbox(char *face, int size, char *text, int length, int *bbox);
+extern int i_wf_text(char *face, i_img *im, int tx, int ty, i_color *cl,
+ int size, char *text, int len, int align, int aa);
+extern int i_wf_cp(char *face, i_img *im, int tx, int ty, int channel,
+ int size, char *text, int len, int align, int aa);
-
+#endif
/* functions for reading and writing formats */
extern i_palidx *quant_translate(i_quantize *quant, i_img *img);
extern void quant_transparent(i_quantize *quant, i_palidx *indices, i_img *img, i_palidx trans_index);
+extern i_img *i_img_pal_new(int x, int y, int channels, int maxpal);
+extern i_img *i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal);
+extern i_img *i_img_to_pal(i_img *src, i_quantize *quant);
+extern i_img *i_img_to_rgb(i_img *src);
+extern i_img *i_img_masked_new(i_img *targ, i_img *mask, int x, int y,
+ int w, int h);
+extern i_img *i_img_16_new(int x, int y, int ch);
+extern i_img *i_img_16_new_low(i_img *im, int x, int y, int ch);
+
#ifdef HAVE_LIBJPEG
i_img *
i_readjpeg_wiol(io_glue *ig, int length, char** iptc_itext, int *itlength);
#endif /* HAVE_LIBPNG */
#ifdef HAVE_LIBGIF
-i_img * i_readgif(int fd, int **colour_table, int *colours);
-i_img * i_readgif_scalar(char *data, int length, int **colour_table, int *colours);
-i_img * i_readgif_callback(i_read_callback_t callback, char *userdata, int **colour_table, int *colours);
+i_img *i_readgif(int fd, int **colour_table, int *colours);
+i_img *i_readgif_scalar(char *data, int length, int **colour_table, int *colours);
+i_img *i_readgif_callback(i_read_callback_t callback, char *userdata, int **colour_table, int *colours);
+extern i_img **i_readgif_multi(int fd, int *count);
+extern i_img **i_readgif_multi_scalar(char *data, int length, int *count);
+extern i_img **i_readgif_multi_callback(i_read_callback_t callback, char *userdata, int *count);
undef_int i_writegif(i_img *im,int fd,int colors,int pixdev,int fixedlen,i_color fixed[]);
undef_int i_writegifmc(i_img *im,int fd,int colors);
undef_int i_writegifex(i_img *im,int fd);
void(*i_arc)(i_img *im,int x,int y,float rad,float d1,float d2,i_color *val);
void(*i_copyto)(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty);
void(*i_copyto_trans)(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans);
- void(*i_rubthru)(i_img *im,i_img *src,int tx,int ty);
+ int(*i_rubthru)(i_img *im,i_img *src,int tx,int ty);
} symbol_table_t;
extern i_failed_cb i_set_failed_cb(i_failed_cb);
extern void i_set_argv0(char const *);
extern int i_set_errors_fatal(int new_fatal);
-extern i_errmsg *i_errors();
+extern i_errmsg *i_errors(void);
extern void i_push_error(int code, char const *msg);
extern void i_push_errorf(int code, char const *fmt, ...);
extern void i_push_errorvf(int code, char const *fmt, va_list);
-extern void i_clear_error();
+extern void i_clear_error(void);
extern int i_failed(int code, char const *msg);
+/* image tag processing */
+extern void i_tags_new(i_img_tags *tags);
+extern int i_tags_addn(i_img_tags *tags, char *name, int code, int idata);
+extern int i_tags_add(i_img_tags *tags, char *name, int code, char *data,
+ int size, int idata);
+extern void i_tags_destroy(i_img_tags *tags);
+extern int i_tags_find(i_img_tags *tags, char *name, int start, int *entry);
+extern int i_tags_findn(i_img_tags *tags, int code, int start, int *entry);
+extern int i_tags_delete(i_img_tags *tags, int entry);
+extern int i_tags_delbyname(i_img_tags *tags, char *name);
+extern int i_tags_delbycode(i_img_tags *tags, int code);
#endif
--- /dev/null
+/* Declares utility functions useful across various files which
+ aren't meant to be available externally
+*/
+
+#ifndef IMAGEI_H_
+#define IMAGEI_H_
+
+#include "image.h"
+
+/* wrapper functions that implement the floating point sample version of a
+ function in terms of the 8-bit sample version
+*/
+extern int i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix);
+extern int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix);
+extern int i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix);
+extern int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix);
+extern int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp,
+ int *chans, int chan_count);
+
+/* wrapper functions that forward palette calls to the underlying image,
+ assuming the underlying image is the first pointer in whatever
+ ext_data points at
+*/
+extern int i_addcolors_forward(i_img *im, i_color *, int count);
+extern int i_getcolors_forward(i_img *im, int i, i_color *, int count);
+extern int i_colorcount_forward(i_img *im);
+extern int i_maxcolors_forward(i_img *im);
+extern int i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry);
+extern int i_setcolors_forward(i_img *im, int index, i_color *colors,
+ int count);
+
+#define SampleFTo16(num) ((int)((num) * 65535.0 + 0.01))
+/* we add that little bit to avoid rounding issues */
+#define Sample16ToF(num) ((num) / 65535.0)
+
+#define SampleFTo8(num) ((int)((num) * 255.0 + 0.01))
+#define Sample8ToF(num) ((num) / 255.0)
+
+#define Sample16To8(num) ((num) / 257)
+#define Sample8To16(num) ((num) * 257)
+
+#endif
--- /dev/null
+/*
+=head1 NAME
+
+img16.c - implements 16-bit images
+
+=head1 SYNOPSIS
+
+ i_img *im = i_img_16_new(int x, int y, int channels);
+ # use like a normal image
+
+=head1 DESCRIPTION
+
+Implements 16-bit/sample images.
+
+This basic implementation is required so that we have some larger
+sample image type to work with.
+
+=over
+
+=cut
+*/
+
+#include "image.h"
+#include "imagei.h"
+
+static int i_ppix_d16(i_img *im, int x, int y, i_color *val);
+static int i_gpix_d16(i_img *im, int x, int y, i_color *val);
+static int i_glin_d16(i_img *im, int l, int r, int y, i_color *vals);
+static int i_plin_d16(i_img *im, int l, int r, int y, i_color *vals);
+static int i_ppixf_d16(i_img *im, int x, int y, i_fcolor *val);
+static int i_gpixf_d16(i_img *im, int x, int y, i_fcolor *val);
+static int i_glinf_d16(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_plinf_d16(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_gsamp_d16(i_img *im, int l, int r, int y, i_sample_t *samps,
+ int *chans, int chan_count);
+static int i_gsampf_d16(i_img *im, int l, int r, int y, i_fsample_t *samps,
+ int *chans, int chan_count);
+
+/*
+=item IIM_base_16bit_direct
+
+Base structure used to initialize a 16-bit/sample image.
+
+Internal.
+
+=cut
+*/
+static i_img IIM_base_16bit_direct =
+{
+ 0, /* channels set */
+ 0, 0, 0, /* xsize, ysize, bytes */
+ ~0, /* ch_mask */
+ i_16_bits, /* bits */
+ i_direct_type, /* type */
+ 0, /* virtual */
+ NULL, /* idata */
+ { 0, 0, NULL }, /* tags */
+ NULL, /* ext_data */
+
+ i_ppix_d16, /* i_f_ppix */
+ i_ppixf_d16, /* i_f_ppixf */
+ i_plin_d16, /* i_f_plin */
+ i_plinf_d16, /* i_f_plinf */
+ i_gpix_d16, /* i_f_gpix */
+ i_gpixf_d16, /* i_f_gpixf */
+ i_glin_d16, /* i_f_glin */
+ i_glinf_d16, /* i_f_glinf */
+ i_gsamp_d16, /* i_f_gsamp */
+ i_gsampf_d16, /* i_f_gsampf */
+
+ NULL, /* i_f_gpal */
+ NULL, /* i_f_ppal */
+ NULL, /* i_f_addcolor */
+ NULL, /* i_f_getcolor */
+ NULL, /* i_f_colorcount */
+ NULL, /* i_f_findcolor */
+
+ NULL, /* i_f_destroy */
+};
+
+/* it's possible some platforms won't have a 16-bit integer type,
+ so we check for one otherwise we work by bytes directly
+
+ We do assume 8-bit char
+*/
+#if __STDC_VERSION__ >= 199901L
+/* C99 should define something useful */
+#include <stdint.h>
+#ifdef UINT16_MAX
+typedef uint16_t i_sample16_t;
+#define GOT16
+#endif
+#endif
+
+/* check out unsigned short */
+#ifndef GOT16
+#include <limits.h>
+#if USHRT_MAX == 65535
+typedef unsigned short i_sample16_t;
+#define GOT16
+#endif
+#endif
+
+#ifdef GOT16
+
+/* we have a real 16-bit unsigned integer */
+#define STORE16(bytes, offset, word) \
+ (((i_sample16_t *)(bytes))[offset] = (word))
+#define STORE8as16(bytes, offset, byte) \
+ (((i_sample16_t *)(bytes))[offset] = (byte) * 256)
+#define GET16(bytes, offset) \
+ (((i_sample16_t *)(bytes))[offset])
+#define GET16as8(bytes, offset) \
+ (((i_sample16_t *)(bytes))[offset] / 256)
+
+#else
+
+/* we have to do this the hard way */
+#define STORE16(bytes, offset, word) \
+ ((((unsigned char *)(bytes))[(offset)*2] = (word) >> 8), \
+ (((unsigned char *)(bytes))[(offset)*2+1] = (word) & 0xFF))
+#define STORE8as16(bytes, offset, byte) \
+ ((((unsigned char *)(bytes))[(offset)*2] = (byte)), \
+ (((unsigned char *)(bytes))[(offset)*2+1] = 0))
+
+#define GET16(bytes, offset) \
+ (((unsigned char *)(bytes))[(offset)*2] * 256 \
+ + ((unsigned char *)(bytes))[(offset)*2+1])
+#define GET16as8(bytes, offset) \
+ (((unsigned char *)(bytes))[(offset)*2] << 8)
+
+#endif
+
+/*
+=item i_img_16_new(int x, int y, int ch)
+
+Creates a new 16-bit per sample image.
+*/
+i_img *i_img_16_new_low(i_img *im, int x, int y, int ch) {
+ mm_log((1,"i_img_16_new(x %d, y %d, ch %d)\n", x, y, ch));
+
+ *im = IIM_base_16bit_direct;
+ i_tags_new(&im->tags);
+ im->xsize = x;
+ im->ysize = y;
+ im->channels = ch;
+ im->bytes = x * y * ch * 2;
+ im->ext_data = NULL;
+ im->idata = mymalloc(im->bytes);
+ if (im->idata) {
+ memset(im->idata, 0, im->bytes);
+ }
+ else {
+ i_tags_destroy(&im->tags);
+ im = NULL;
+ }
+
+ return im;
+}
+
+i_img *i_img_16_new(int x, int y, int ch) {
+ i_img *im;
+
+ im = mymalloc(sizeof(i_img));
+ if (im) {
+ if (!i_img_16_new_low(im, x, y, ch)) {
+ myfree(im);
+ im = NULL;
+ }
+ }
+
+ mm_log((1, "(%p) <- i_img_16_new\n", im));
+
+ return im;
+}
+
+static int i_ppix_d16(i_img *im, int x, int y, i_color *val) {
+ int off, ch;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y > im->ysize)
+ return -1;
+
+ off = (x + y * im->xsize) * im->channels;
+ for (ch = 0; ch < im->channels; ++ch)
+ STORE8as16(im->idata, off+ch, val->channel[ch]);
+
+ return 0;
+}
+
+static int i_gpix_d16(i_img *im, int x, int y, i_color *val) {
+ int off, ch;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y > im->ysize)
+ return -1;
+
+ off = (x + y * im->xsize) * im->channels;
+ for (ch = 0; ch < im->channels; ++ch)
+ val->channel[ch] = GET16as8(im->idata, off+ch);
+
+ return 0;
+}
+
+static int i_ppixf_d16(i_img *im, int x, int y, i_fcolor *val) {
+ int off, ch;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y > im->ysize)
+ return -1;
+
+ off = (x + y * im->xsize) * im->channels;
+ for (ch = 0; ch < im->channels; ++ch)
+ STORE16(im->idata, off+ch, SampleFTo16(val->channel[ch]));
+
+ return 0;
+}
+
+static int i_gpixf_d16(i_img *im, int x, int y, i_fcolor *val) {
+ int off, ch;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y > im->ysize)
+ return -1;
+
+ off = (x + y * im->xsize) * im->channels;
+ for (ch = 0; ch < im->channels; ++ch)
+ val->channel[ch] = Sample16ToF(GET16(im->idata, off+ch));
+
+ return 0;
+}
+
+static int i_glin_d16(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ int off;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ vals[i].channel[ch] = GET16as8(im->idata, off);
+ ++off;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_plin_d16(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ int off;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ STORE8as16(im->idata, off, vals[i].channel[ch]);
+ ++off;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_glinf_d16(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ int ch, count, i;
+ int off;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ vals[i].channel[ch] = Sample16ToF(GET16(im->idata, off));
+ ++off;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_plinf_d16(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ int ch, count, i;
+ int off;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ for (ch = 0; ch < im->channels; ++ch) {
+ STORE16(im->idata, off, SampleFTo16(vals[i].channel[ch]));
+ ++off;
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_gsamp_d16(i_img *im, int l, int r, int y, i_sample_t *samps,
+ int *chans, int chan_count) {
+ int ch, count, i, w;
+ int off;
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = GET16as8(im->idata, off+chans[ch]);
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+ else {
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = GET16as8(im->idata, off+ch);
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_gsampf_d16(i_img *im, int l, int r, int y, i_fsample_t *samps,
+ int *chans, int chan_count) {
+ int ch, count, i, w;
+ int off;
+
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ off = (l+y*im->xsize) * im->channels;
+ w = r - l;
+ count = 0;
+
+ if (chans) {
+ /* make sure we have good channel numbers */
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ return 0;
+ }
+ }
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = Sample16ToF(GET16(im->idata, off+chans[ch]));
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+ else {
+ for (i = 0; i < w; ++i) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = Sample16ToF(GET16(im->idata, off+ch));
+ ++count;
+ }
+ off += im->channels;
+ }
+ }
+
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
#else
#define malloc_comm(a,b) (mymalloc(a))
-void malloc_state();
+void malloc_state(void);
void* mymalloc(int size);
void myfree(void *p);
+void *myrealloc(void *p, size_t newsize);
#endif /* IMAGER_MALLOC_DEBUG */
mm_log((1, "mymalloc(size %d)\n", size));
if ( (buf = malloc(size)) == NULL ) {
- mm_log((1, "mymalloc: unable to malloc\n", size));
+ mm_log((1, "mymalloc: unable to malloc %d\n", size));
fprintf(stderr,"Unable to malloc.\n"); exit(3);
}
return buf;
free(p);
}
+void *
+myrealloc(void *block, size_t size) {
+ void *result;
+
+ mm_log((1, "myrealloc(block %p, size %u)\n", block, size));
+ if ((result = realloc(block, size)) == NULL) {
+ mm_log((1, "myrealloc: out of memory\n"));
+ fprintf(stderr, "Out of memory.\n");
+ exit(3);
+ }
+ return result;
+}
+
#endif /* IMAGER_MALLOC_DEBUG */
static
io_blink*
-io_blink_new() {
+io_blink_new(void) {
io_blink *ib;
mm_log((1, "io_blink_new()\n"));
/* XS functions */
io_glue *io_new_fd(int fd);
-io_glue *io_new_bufchain();
+io_glue *io_new_bufchain(void);
size_t io_slurp(io_glue *ig, unsigned char **c);
void io_glue_DESTROY(io_glue *ig);
buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
while (cinfo.output_scanline < cinfo.output_height) {
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
- memcpy(im->data+im->channels*im->xsize*(cinfo.output_scanline-1),buffer[0],row_stride);
+ memcpy(im->idata+im->channels*im->xsize*(cinfo.output_scanline-1),buffer[0],row_stride);
}
(void) jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
if (!(im->channels==1 || im->channels==3)) { fprintf(stderr,"Unable to write JPEG, improper colorspace.\n"); exit(3); }
quality = qfactor;
- image_buffer = im->data;
-
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
row_stride = im->xsize * im->channels; /* JSAMPLEs per row in image_buffer */
- while (cinfo.next_scanline < cinfo.image_height) {
- row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
- (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
+ image_buffer=im->idata;
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ /* jpeg_write_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could pass
+ * more than one scanline at a time if that's more convenient.
+ */
+ row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+ }
+ else {
+ unsigned char *data = mymalloc(im->xsize * im->channels);
+ if (data) {
+ while (cinfo.next_scanline < cinfo.image_height) {
+ /* jpeg_write_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could pass
+ * more than one scanline at a time if that's more convenient.
+ */
+ i_gsamp(im, 0, im->xsize, cinfo.next_scanline, data,
+ NULL, im->channels);
+ row_pointer[0] = data;
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+ }
+ else {
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ return 0; /* out of memory? */
+ }
}
/* Step 6: Finish compression */
# It's just a front end to the XS creation functions.
+# used in converting hsv to rgb
+my @hsv_map =
+ (
+ 'vkm', 'nvm', 'mvk', 'mnv', 'kmv', 'vmn'
+ );
+
+sub _hsv_to_rgb {
+ my ($hue, $sat, $val) = @_;
+
+ # HSV conversions from pages 401-403 "Procedural Elements for Computer
+ # Graphics", 1985, ISBN 0-07-053534-5.
+
+ my @result;
+ if ($sat <= 0) {
+ return ( 255 * $val, 255 * $val, 255 * $val );
+ }
+ else {
+ $val >= 0 or $val = 0;
+ $val <= 1 or $val = 1;
+ $sat <= 1 or $sat = 1;
+ $hue >= 360 and $hue %= 360;
+ $hue < 0 and $hue += 360;
+ $hue /= 60.0;
+ my $i = int($hue);
+ my $f = $hue - $i;
+ $val *= 255;
+ my $m = $val * (1.0 - $sat);
+ my $n = $val * (1.0 - $sat * $f);
+ my $k = $val * (1.0 - $sat * (1 - $f));
+ my $v = $val;
+ my %fields = ( m=>$m, n=>$n, v=>$v, k=>$k, );
+ return @fields{split //, $hsv_map[$i]};
+ }
+}
+
+# cache of loaded gimp files
+# each key is a filename, under each key is a hashref with the following
+# keys:
+# mod_time => last mod_time of file
+# colors => hashref name to arrayref of colors
+my %gimp_cache;
+
+# palette search locations
+# this is pretty rude
+# $HOME is replaced at runtime
+my @gimp_search =
+ (
+ '$HOME/.gimp-1.2/palettes/Named_Colors',
+ '$HOME/.gimp-1.1/palettes/Named_Colors',
+ '$HOME/.gimp/palettes/Named_Colors',
+ '/usr/share/gimp/1.2/palettes/Named_Colors',
+ '/usr/share/gimp/1.1/palettes/Named_Colors',
+ '/usr/share/gimp/palettes/Named_Colors',
+ );
+
+sub _load_gimp_palette {
+ my ($filename) = @_;
+
+ if (open PAL, "< $filename") {
+ my $hdr = <PAL>;
+ chomp $hdr;
+ unless ($hdr =~ /GIMP Palette/) {
+ close PAL;
+ $Imager::ERRSTR = "$filename is not a GIMP palette file";
+ return;
+ }
+ my $line;
+ my %pal;
+ my $mod_time = (stat PAL)[9];
+ while (defined($line = <PAL>)) {
+ next if $line =~ /^#/ || $line =~ /^\s*$/;
+ chomp $line;
+ my ($r,$g, $b, $name) = split ' ', $line, 4;
+ if ($name) {
+ $name =~ s/\s*\([\d\s]+\)\s*$//;
+ $pal{lc $name} = [ $r, $g, $b ];
+ }
+ }
+ close PAL;
+
+ $gimp_cache{$filename} = { mod_time=>$mod_time, colors=>\%pal };
+
+ return 1;
+ }
+ else {
+ $Imager::ERRSTR = "Cannot open palette file $filename: $!";
+ return;
+ }
+}
+
+sub _get_gimp_color {
+ my %args = @_;
+
+ my $filename;
+ if ($args{palette}) {
+ $filename = $args{palette};
+ }
+ else {
+ # try to make one up - this is intended to die if tainting is
+ # enabled and $ENV{HOME} is tainted. To avoid that untaint $ENV{HOME}
+ # or set the palette parameter
+ for my $attempt (@gimp_search) {
+ my $work = $attempt; # don't modify the source array
+ $work =~ s/\$HOME/$ENV{HOME}/;
+ if (-e $work) {
+ $filename = $work;
+ last;
+ }
+ }
+ if (!$filename) {
+ $Imager::ERRSTR = "No GIMP palette found";
+ return ();
+ }
+ }
+
+ if ((!$gimp_cache{$filename}
+ || (stat $filename)[9] != $gimp_cache{$filename})
+ && !_load_gimp_palette($filename)) {
+ return ();
+ }
+
+ if (!$gimp_cache{$filename}{colors}{lc $args{name}}) {
+ $Imager::ERRSTR = "Color '$args{name}' isn't in $filename";
+ return ();
+ }
+
+ return @{$gimp_cache{$filename}{colors}{lc $args{name}}};
+}
+
+my @x_search =
+ (
+ '/usr/lib/X11/rgb.txt', # seems fairly standard
+ '/usr/local/lib/X11/rgb.txt', # seems possible
+ '/usr/X11R6/lib/X11/rgb.txt', # probably the same as the first
+ );
+
+# x rgb.txt cache
+# same structure as %gimp_cache
+my %x_cache;
+
+sub _load_x_rgb {
+ my ($filename) = @_;
+
+ local *RGB;
+ if (open RGB, "< $filename") {
+ my $line;
+ my %pal;
+ my $mod_time = (stat RGB)[9];
+ while (defined($line = <RGB>)) {
+ # the version of rgb.txt supplied with GNU Emacs uses # for comments
+ next if $line =~ /^[!#]/ || $line =~ /^\s*$/;
+ chomp $line;
+ my ($r,$g, $b, $name) = split ' ', $line, 4;
+ if ($name) {
+ $pal{lc $name} = [ $r, $g, $b ];
+ }
+ }
+ close RGB;
+
+ $x_cache{$filename} = { mod_time=>$mod_time, colors=>\%pal };
+
+ return 1;
+ }
+ else {
+ $Imager::ERRSTR = "Cannot open palette file $filename: $!";
+ return;
+ }
+}
+
+sub _get_x_color {
+ my %args = @_;
+
+ my $filename;
+ if ($args{palette}) {
+ $filename = $args{palette};
+ }
+ else {
+ for my $attempt (@x_search) {
+ if (-e $attempt) {
+ $filename = $attempt;
+ last;
+ }
+ }
+ if (!$filename) {
+ $Imager::ERRSTR = "No X rgb.txt palette found";
+ return ();
+ }
+ }
+
+ if ((!$x_cache{$filename}
+ || (stat $filename)[9] != $x_cache{$filename})
+ && !_load_x_rgb($filename)) {
+ return ();
+ }
+
+ if (!$x_cache{$filename}{colors}{lc $args{name}}) {
+ $Imager::ERRSTR = "Color '$args{name}' isn't in $filename";
+ return ();
+ }
+
+ return @{$x_cache{$filename}{colors}{lc $args{name}}};
+}
# Parse color spec into an a set of 4 colors
sub pspec {
- return (@_,255) if @_ == 3;
- return (@_ ) if @_ == 4;
+ return (@_,255) if @_ == 3 && !grep /[^\d.+eE-]/, @_;
+ return (@_ ) if @_ == 4 && !grep /[^\d.+eE-]/, @_;
if ($_[0] =~
/^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
return (hex($1),hex($2),hex($3),hex($4));
if ($_[0] =~ /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
return (hex($1),hex($2),hex($3),255);
}
+ if ($_[0] =~ /^\#([\da-f])([\da-f])([\da-f])$/i) {
+ return (hex($1) * 17, hex($2) * 17, hex($3) * 17, 255);
+ }
+ my %args;
+ if (@_ == 1) {
+ # a named color
+ %args = ( name => @_ );
+ }
+ else {
+ %args = @_;
+ }
+ my @result;
+ if (exists $args{gray}) {
+ @result = $args{gray};
+ }
+ elsif (exists $args{grey}) {
+ @result = $args{grey};
+ }
+ elsif ((exists $args{red} || exists $args{r})
+ && (exists $args{green} || exists $args{g})
+ && (exists $args{blue} || exists $args{b})) {
+ @result = ( exists $args{red} ? $args{red} : $args{r},
+ exists $args{green} ? $args{green} : $args{g},
+ exists $args{blue} ? $args{blue} : $args{b} );
+ }
+ elsif ((exists $args{hue} || exists $args{h})
+ && (exists $args{saturation} || exists $args{'s'})
+ && (exists $args{value} || exists $args{v})) {
+ my $hue = exists $args{hue} ? $args{hue} : $args{h};
+ my $sat = exists $args{saturation} ? $args{saturation} : $args{'s'};
+ my $val = exists $args{value} ? $args{value} : $args{v};
+
+ @result = _hsv_to_rgb($hue, $sat, $val);
+ }
+ elsif (exists $args{web}) {
+ if ($args{web} =~ /^#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])$/i) {
+ @result = (hex($1),hex($2),hex($3));
+ }
+ elsif ($args{web} =~ /^#?([\da-f])([\da-f])([\da-f])$/i) {
+ @result = (hex($1) * 17, hex($2) * 17, hex($3) * 17);
+ }
+ }
+ elsif ($args{name}) {
+ unless (@result = _get_gimp_color(%args)) {
+ unless (@result = _get_x_color(%args)) {
+ $Imager::ERRSTR = "No color named $args{name} found";
+ return ();
+ }
+ }
+ }
+ elsif ($args{gimp}) {
+ @result = _get_gimp_color(name=>$args{gimp}, %args);
+ }
+ elsif ($args{xname}) {
+ @result = _get_x_color(name=>$args{xname}, %args);
+ }
+ elsif ($args{rgb}) {
+ @result = @{$args{rgb}};
+ }
+ elsif ($args{rgba}) {
+ @result = @{$args{rgba}};
+ return @result if @result == 4;
+ }
+ elsif ($args{hsv}) {
+ @result = _hsv_to_rgb(@{$args{hsv}});
+ }
+ elsif ($args{channels}) {
+ return @{$args{channels}};
+ }
+ elsif (exists $args{channel0} || $args{c0}) {
+ my $i = 0;
+ while (exists $args{"channel$i"} || exists $args{"c$i"}) {
+ push(@result,
+ exists $args{"channel$i"} ? $args{"channel$i"} : $args{"c$i"});
+ ++$i;
+ }
+ }
+ else {
+ $Imager::ERRSTR = "No color specification found";
+ return ();
+ }
+ if (@result) {
+ if (exists $args{alpha} || exists $args{a}) {
+ push(@result, exists $args{alpha} ? $args{alpha} : $args{a});
+ }
+ while (@result < 4) {
+ push(@result, 255);
+ }
+ return @result;
+ }
return ();
}
-
-
sub new {
shift; # get rid of class name.
my @arg = pspec(@_);
return @arg ? set_internal($self, $arg[0],$arg[1],$arg[2],$arg[3]) : ();
}
-
-
-
-
1;
__END__
=back
+You can specify colors in several different ways, you can just supply
+simple values:
+
+=over
+
+=item *
+
+simple numeric parameters - if you supply 3 or 4 numeric arguments, you get a color made up of those RGB (and possibly A) components.
+
+=item *
+
+a six hex digit web color, either 'RRGGBB' or '#RRGGBB'
+
+=item *
+
+an eight hex digit web color, either 'RRGGBBAA' or '#RRGGBBAA'.
+
+=item *
+
+a 3 hex digit web color, '#RGB' - a value of F becomes 255.
+
+=item *
+
+a color name, from whichever of the gimp Named_Colors file or X
+rgb.txt is found first. The same as using the name keyword.
+
+=back
+
+You can supply named parameters:
+
+=over
+
+=item *
+
+'red', 'green' and 'blue', optionally shortened to 'r', 'g' and 'b'.
+The color components in the range 0 to 255.
+
+ # all of the following are equivalent
+ my $c1 = Imager::Color->new(red=>100, blue=>255, green=>0);
+ my $c2 = Imager::Color->new(r=>100, b=>255, g=>0);
+ my $c3 = Imager::Color->new(r=>100, blue=>255, g=>0);
+
+=item *
+
+'hue', 'saturation' and 'value', optionally shortened to 'h', 's' and
+'v', to specify a HSV color. 0 <= hue < 360, 0 <= s <= 1 and 0 <= v
+<= 1.
+
+ # the same as RGB(127,255,127)
+ my $c1 = Imager::Color->new(hue=>120, v=>1, s=>0.5);
+ my $c1 = Imager::Color->new(hue=>120, value=>1, saturation=>0.5);
+
+=item *
+
+'web', which can specify a 6 or 3 hex digit web color, in any of the
+forms '#RRGGBB', '#RGB', 'RRGGBB' or 'RGB'.
+
+ my $c1 = Imager::Color->new(web=>'#FFC0C0'); # pale red
+
+=item *
+
+'gray' or 'grey' which specifies a single channel, from 0 to 255.
+
+ # exactly the same
+ my $c1 = Imager::Color->new(gray=>128);
+ my $c1 = Imager::Color->new(grey=>128);
+
+=item *
+
+'rgb' which takes a 3 member arrayref, containing each of the red,
+green and blue values.
+
+ # the same
+ my $c1 = Imager::Color->new(rgb=>[255, 100, 0]);
+ my $c1 = Imager::Color->new(r=>255, g=>100, b=>0);
+
+=item *
+
+'hsv' which takes a 3 member arrayref, containting each of hue,
+saturation and value.
+
+ # the same
+ my $c1 = Imager::Color->new(hsv=>[120, 0.5, 1]);
+ my $c1 = Imager::Color->new(hue=>120, v=>1, s=>0.5);
+
+=item *
+
+'gimp' which specifies a color from a GIMP palette file. You can
+specify the filename of the palette file with the 'palette' parameter,
+or let Imager::Color look in various places, typically
+"$HOME/gimp-1.x/palettes/Named_Colors" with and without the version
+number, and in /usr/share/gimp/palettes/. The palette file must have
+color names.
+
+ my $c1 = Imager::Color->new(gimp=>'snow');
+ my $c1 = Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal);
+
+=item *
+
+'xname' which specifies a color from an X11 rgb.txt file. You can
+specify the filename of the rgb.txt file with the 'palette' parameter,
+or let Imager::Color look in various places, typically
+'/usr/lib/X11/rgb.txt'.
+
+ my $c1 = Imager::Color->new(xname=>'blue') # usually RGB(0, 0, 255)
+
+=item *
+
+'name' which specifies a name from either a GIMP palette or an X
+rgb.txt file, whichever is found first.
+
+=item *
+
+'channel0', 'channel1', etc, each of which specifies a single channel. These can be abbreviated to 'c0', 'c1' etc.
+
+=item *
+
+'channels' which takes an arrayref of the channel values.
+
+=back
+
+Optionally you can add an alpha channel to a color with the 'alpha' or
+'a' parameter.
+
+These color specifications can be used for both constructing new
+colors with the new() method and modifying existing colors with the
+set() method.
+
=head1 AUTHOR
Arnar M. Hrafnkelsson, addi@umich.edu
http://www.eecs.umich.edu/~addi/perl/Imager/
=cut
-
--- /dev/null
+package Imager::Color::Float;
+
+use Imager;
+use strict;
+use vars qw();
+
+# It's just a front end to the XS creation functions.
+
+
+# Parse color spec into an a set of 4 colors
+
+sub pspec {
+ return (@_,1) if @_ == 3;
+ return (@_ ) if @_ == 4;
+ if ($_[0] =~
+ /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
+ return (hex($1)/255.99,hex($2)/255.99,hex($3)/255.99,hex($4)/255.99);
+ }
+ if ($_[0] =~ /^\#?([\da-f][\da-f])([\da-f][\da-f])([\da-f][\da-f])/i) {
+ return (hex($1)/255.99,hex($2)/255.99,hex($3)/255.99,1);
+ }
+ return ();
+}
+
+
+
+sub new {
+ shift; # get rid of class name.
+ my @arg = pspec(@_);
+ return @arg ? new_internal($arg[0],$arg[1],$arg[2],$arg[3]) : ();
+}
+
+sub set {
+ my $self = shift;
+ my @arg = pspec(@_);
+ return @arg ? set_internal($self, $arg[0],$arg[1],$arg[2],$arg[3]) : ();
+}
+
+
+
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::Color::Float - Rough floating point sample colour handling
+
+=head1 SYNOPSIS
+
+ $color = Imager::Color->new($red, $green, $blue);
+ $color = Imager::Color->new($red, $green, $blue, $alpha);
+ $color = Imager::Color->new("#C0C0FF"); # html color specification
+
+ $color->set($red, $green, $blue);
+ $color->set($red, $green, $blue, $alpha);
+ $color->set("#C0C0FF"); # html color specification
+
+ ($red, $green, $blue, $alpha) = $color->rgba();
+ @hsv = $color->hsv(); # not implemented but proposed
+
+ $color->info();
+
+
+=head1 DESCRIPTION
+
+This module handles creating color objects used by imager. The idea is
+that in the future this module will be able to handle colorspace calculations
+as well.
+
+=over 4
+
+=item new
+
+This creates a color object to pass to functions that need a color argument.
+
+=item set
+
+This changes an already defined color. Note that this does not affect any places
+where the color has been used previously.
+
+=item rgba
+
+This returns the rgba code of the color the object contains.
+
+=item info
+
+Calling info merely dumps the relevant colorcode to the log.
+
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson, addi@umich.edu
+And a great deal of help from others - see the README for a complete
+list.
+
+=head1 SEE ALSO
+
+Imager(3)
+http://www.eecs.umich.edu/~addi/perl/Imager/
+
+=cut
+
+
package Imager::Font;
use Imager::Color;
use strict;
use File::Spec;
-# This class is a container
-# and works for both truetype and t1 fonts.
-
+# the aim here is that we can:
+# - add file based types in one place: here
+# - make sure we only attempt to create types that exist
+# - give reasonable defaults
+# - give the user some control over which types get used
+my %drivers =
+ (
+ tt=>{
+ class=>'Imager::Font::Truetype',
+ module=>'Imager/Font/Truetype.pm',
+ files=>'.*\.ttf$',
+ },
+ t1=>{
+ class=>'Imager::Font::Type1',
+ module=>'Imager/Font/Type1.pm',
+ files=>'.*\.pfb$',
+ },
+ ft2=>{
+ class=>'Imager::Font::FreeType2',
+ module=>'Imager/Font/FreeType2.pm',
+ files=>'.*\.(pfb|ttf|fon)$', # maybe this should be dynamic
+ },
+ w32=>{
+ class=>'Imager::Font::Win32',
+ module=>'Imager/Font/Win32.pm',
+ },
+ );
+
+# this currently should only contain file based types, don't add w32
+my @priority = qw(t1 tt ft2);
+
+# 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;
+ delete @drivers{grep !Imager::i_has_format($_), keys %drivers};
+}
# search method
# 1. start by checking if file is the parameter
}
$type=$hsh{'type'};
- if (!defined($type) or $type !~ m/^(t1|tt)/) {
- $type='tt' if $file =~ m/\.ttf$/i;
- $type='t1' if $file =~ m/\.pfb$/i;
+ if (!defined($type) or !$drivers{$type}) {
+ for my $drv (@priority) {
+ undef $type;
+ my $re = $drivers{$drv}{files} or next;
+ if ($file =~ /$re/i) {
+ $type = $drv;
+ last;
+ }
+ }
}
if (!defined($type)) {
$Imager::ERRSTR="Font type not found";
return;
}
+ } elsif ($hsh{face}) {
+ $type = "w32";
} else {
$Imager::ERRSTR="No font file specified";
return;
# here we should have the font type or be dead already.
- if ($type eq 't1') {
- require 'Imager/Font/Type1.pm';
- return Imager::Font::Type1->new(%hsh);
- }
-
- if ($type eq 'tt') {
- require 'Imager/Font/Truetype.pm';
- return Imager::Font::Truetype->new(%hsh);
- }
- # it would be nice to have some generic mechanism to select the
- # class
-
- return undef;
+ require $drivers{$type}{module};
+ return $drivers{$type}{class}->new(%hsh);
}
# returns first defined parameter
return undef;
}
$input{align} = _first($input{align}, 1);
+ $input{utf8} = _first($input{utf8}, $self->{utf8}, 0);
+ $input{vlayout} = _first($input{vlayout}, $self->{vlayout}, 0);
+
$self->_draw(%input);
}
return @box;
}
+sub dpi {
+ my $self = shift;
+
+ # I'm assuming a default of 72 dpi
+ my @old = (72, 72);
+ if (@_) {
+ $Imager::ERRSTR = "Setting dpi not implemented for this font type";
+ return;
+ }
+
+ return @old;
+}
+
+sub transform {
+ my $self = shift;
+
+ my %hsh = @_;
+
+ # this is split into transform() and _transform() so we can
+ # implement other tags like: degrees=>12, which would build a
+ # 12 degree rotation matrix
+ # but I'll do that later
+ unless ($hsh{matrix}) {
+ $Imager::ERRSTR = "You need to supply a matrix";
+ return;
+ }
+
+ return $self->_transform(%hsh);
+}
+
+sub _transform {
+ $Imager::ERRSTR = "This type of font cannot be transformed";
+ return;
+}
+
+sub utf8 {
+ return 0;
+}
+
+sub priorities {
+ my $self = shift;
+ my @old = @priority;
+
+ if (@_) {
+ @priority = grep Imager::i_has_format($_), @_;
+ }
+ return @old;
+}
+
1;
__END__
$t1font = Imager::Font->new(file => 'pathtofont.pfb');
$ttfont = Imager::Font->new(file => 'pathtofont.ttf');
+ $w32font = Imager::Font->new(face => 'Times New Roman');
$blue = Imager::Color->new("#0000FF");
$font = Imager::Font->new(file => 'pathtofont.ttf',
use Imager;
print "Has truetype" if $Imager::formats{tt};
print "Has t1 postscript" if $Imager::formats{t1};
-
+ print "Has Win32 fonts" if $Imager::formats{w32};
+ print "Has Freetype2" if $Imager::formats{ft2};
=over 4
size => 15
aa => 0
+To use Win32 fonts supply the facename of the font:
+
+ $font = Imager::Font->new(face=>'Arial Bold Italic');
+
+There isn't any access to other logical font attributes, but this
+typically isn't necessary for Win32 TrueType fonts, since you can
+contruct the full name of the font as above.
+
+Other logical font attributes may be added if there is sufficient demand.
+
=item bounding_box
+
Returns the bounding box for the specified string. Example:
($neg_width,
the string function they are used instead of the defaults stored in
the font.
+The following parameters can be supplied to the string() method:
+
+=over
+
+=item string
+
+The text to be rendered. If this isn't present the 'text' parameter
+is used. If neither is present the call will fail.
+
+=item aa
+
+If non-zero the output will be anti-aliased.
+
+=item x
+
+=item y
+
+The start point for rendering the text. See the align parameter.
+
+=item align
+
+If non-zero the point supplied in (x,y) will be on the base-line, if
+zero then (x,y) will be at the top-left of the first character.
+
+=item channel
+
+If present, the text will be written to the specified channel of the
+image and the color parameter will be ignore.
+
+=item color
+
+The color to draw the text in.
+
+=item size
+
+The point-size to draw the text at.
+
+=item sizew
+
+For drivers that support it, the width to draw the text at. Defaults
+to be value of the 'size' parameter.
+
+=item utf8
+
+For drivers that support it, treat the string as UTF8 encoded. For
+versions of perl that support Unicode (5.6 and later), this will be
+enabled automatically if the 'string' parameter is already a UTF8
+string. See L<UTF8> for more information.
+
+=item vlayout
+
+For drivers that support it, draw the text vertically. Note: I
+haven't found a font that has the appropriate metrics yet.
+
+=back
+
If string() is called with the C<channel> parameter then the color
isn't used and the font is drawn in only one channel of the image.
This can be quite handy to create overlays. See the examples for tips
rendering it. The bounding_box() method described earlier can be used
for that.
+=item dpi()
+
+=item dpi(xdpi=>$xdpi, ydpi=>$ydpi)
+
+=item dpi(dpi=>$dpi)
+
+Set retrieve the spatial resolution of the image in dots per inch.
+The default is 72 dpi.
+
+This isn't implemented for all font types yet.
+
+=item transform(matrix=>$matrix)
+
+Applies a transformation to the font, where matrix is an array ref of
+numbers representing a 2 x 3 matrix:
+
+ [ $matrix->[0], $matrix->[1], $matrix->[2],
+ $matrix->[3], $matrix->[4], $matrix->[5] ]
+
+Not all font types support transformations, these will return false.
+
+It's possible that a driver will disable hinting if you use a
+transformation, to prevent discontinuities in the transformations.
+See the end of the test script t/t38ft2font.t for an example.
=item logo
->string(text=>"Plan XYZ", border=>5)
->write(file=>"xyz.png");
+=back
+
+=head1 UTF8
+
+There are 2 ways of rendering Unicode characters with Imager:
+=over
+
+=item *
+
+For versions of perl that support it, use perl's native UTF8 strings.
+This is the simplest method.
+
+=item *
+
+Hand build your own UTF8 encoded strings. Only recommended if your
+version of perl has no UTF8 support.
=back
+Imager won't construct characters for you, so if want to output
+unicode character 00C3 "LATIN CAPITAL LETTER A WITH DIAERESIS", and
+your font doesn't support it, Imager will I<not> build it from 0041
+"LATIN CAPITAL LETTER A" and 0308 "COMBINING DIAERESIS".
+
+=head2 Native UTF8 Support
+
+If your version of perl supports UTF8 and the driver supports UTF8,
+just use the $im->string() method, and it should do the right thing.
+
+=head2 Build your own
+
+In this case you need to build your own UTF8 encoded characters.
+
+For example:
+
+ $x = pack("C*", 0xE2, 0x80, 0x90); # character code 0x2010 HYPHEN
+
+You need to be be careful with versions of perl that have UTF8
+support, since your string may end up doubly UTF8 encoded.
+
+For example:
+
+ $x = "A\xE2\x80\x90\x41\x{2010}";
+ substr($x, -1, 0) = "";
+ # at this point $x is has the UTF8 flag set, but has 5 characters,
+ # none, of which is the constructed UTF8 character
+
+The test script t/t38ft2font.t has a small example of this after the
+comment:
+
+ # an attempt using emulation of UTF8
+
+=head1 DRIVER CONTROL
+
+If you don't supply a 'type' parameter to Imager::Font->new(), but you
+do supply a 'file' parameter, Imager will attempt to guess which font
+driver to used based on the extension of the font file.
+
+Since some formats can be handled by more than one driver, a priority
+list is used to choose which one should be used, if a given format can
+be handled by more than one driver.
+
+The current priority can be retrieved with:
+
+ @drivers = Imager::Font->priorities();
+
+You can set new priorities and save the old priorities with:
+
+ @old = Imager::Font->priorities(@drivers);
+
+If you supply driver names that are not currently supported, they will
+be ignored.
+
+Imager supports both T1Lib and Freetype2 for working with Type 1
+fonts, but currently only T1Lib does any caching, so by default T1Lib
+is given a higher priority. Since Imager's Freetype2 support can also
+do font transformations, you may want to give that a higher priority:
+
+ my @old = Imager::Font->priorities(qw(tt ft2 t1));
+
=head1 AUTHOR
Arnar M. Hrafnkelsson, addi@umich.edu
--- /dev/null
+package Imager::Font::FreeType2;
+use strict;
+use Imager::Color;
+use vars qw(@ISA);
+@ISA = qw(Imager::Font);
+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});
+}
+
+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;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+ Imager::Font::FreeType2 - low-level functions for FreeType2 text output
+
+=head1 DESCRIPTION
+
+Imager::Font creates a Imager::Font::FreeType2 object when asked to.
+
+See Imager::Font to see how to use this type.
+
+This class provides low-level functions that require the caller to
+perform data validation.
+
+This driver supports:
+
+=over
+
+=item transform()
+
+=item dpi()
+
+=item draw()
+
+The following parameters:
+
+=over
+
+=item utf8
+
+=item vlayour
+
+=item sizew
+
+=back
+
+=back
+
+=head2 Special behaviors
+
+If you call transform() to set a transformation matrix, hinting will
+be switched off. This prevents sudden jumps in the size of the text
+caused by the hinting when the transformation is the identity matrix.
+If for some reason you want hinting enabled, use
+$font->hinting(hinting=>1) to re-enable hinting. This will need to be
+called after I<each> call to transform().
+
+=head1 AUTHOR
+
+Addi, Tony
+
+=cut
--- /dev/null
+package Imager::Font::Win32;
+use strict;
+use vars qw(@ISA);
+@ISA = qw(Imager::Font);
+
+# called by Imager::Font::new()
+# since Win32's HFONTs include the size information this
+# is just a stub
+sub new {
+ my ($class, %opts) = @_;
+
+ return bless \%opts, $class;
+}
+
+sub _bounding_box {
+ my ($self, %opts) = @_;
+
+ my @bbox = i_wf_bbox($self->{face}, $opts{size}, $opts{string});
+}
+
+sub _draw {
+ my $self = shift;
+
+ my %input = @_;
+ if (exists $input{channel}) {
+ Imager::i_wf_cp($self->{face}, $input{image}{IMG}, $input{x}, $input{'y'},
+ $input{channel}, $input{size},
+ $input{string}, $input{align}, $input{aa});
+ }
+ else {
+ Imager::i_wf_text($self->{face}, $input{image}{IMG}, $input{x},
+ $input{'y'}, $input{color}, $input{size},
+ $input{string}, $input{align}, $input{aa});
+ }
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::Font::Win32 - uses Win32 GDI services for text output
+
+=head1 SYNOPSIS
+
+ my $font = Imager::Font->new(face=>"Arial");
+
+=head1 DESCRIPTION
+
+Implements font support using Win32 GDI calls. See Imager::Font for
+usage information.
+
+=cut
--- /dev/null
+package Imager::Matrix2d;
+use strict;
+
+=head1 NAME
+
+ Imager::Matrix2d - simple wrapper for matrix construction
+
+=head1 SYNOPSIS
+
+ use Imager::Matrix2d;
+ $m1 = Imager::Matrix2d->identity;
+ $m2 = Imager::Matrix2d->rotate(radians=>$angle, x=>$cx, y=>$cy);
+ $m3 = Imager::Matrix2d->translate(x=>$dx, y=>$dy);
+ $m4 = Imager::Matrix2d->shear(x=>$sx, y=>$sy);
+ $m5 = Imager::Matrix2d->reflect(axis=>$axis);
+ $m6 = Imager::Matrix2d->scale(x=>$xratio, y=>$yratio);
+ $m6 = $m1 * $m2;
+ $m7 = $m1 + $m2;
+ use Imager::Matrix2d qw(:handy);
+ # various m2d_* functions imported
+ # where m2d_(.*) calls Imager::Matrix2d->$1()
+
+=head1 DESCRIPTION
+
+This class provides a simple wrapper around a reference to an array of
+9 co-efficients, treated as a matrix:
+
+ [ 0, 1, 2,
+ 3, 4, 5,
+ 6, 7, 8 ]
+
+Most of the methods in this class are constructors. The others are
+overloaded operators.
+
+Note that since Imager represents images with y increasing from top to
+bottom, rotation angles are clockwise, rather than counter-clockwise.
+
+=over
+
+=cut
+
+use vars qw(@EXPORT_OK %EXPORT_TAGS @ISA);
+@ISA = 'Exporter';
+require 'Exporter.pm';
+@EXPORT_OK = qw(m2d_rotate m2d_identity m2d_translate m2d_shear
+ m2d_reflect m2d_scale);
+%EXPORT_TAGS =
+ (
+ handy=> [ qw(m2d_rotate m2d_identity m2d_translate m2d_shear
+ m2d_reflect m2d_scale) ],
+ );
+
+use overload
+ '*' => \&_mult,
+ '+' => \&_add,
+ '""'=>\&_string;
+
+=item identity()
+
+Returns the identity matrix.
+
+=cut
+
+sub identity {
+ return bless [ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 ], $_[0];
+}
+
+=item rotate(radians=>$angle)
+
+=item rotate(degrees=>$angle)
+
+Creates a matrix that rotates around the origin, or around the point
+(x,y) if the 'x' and 'y' parameters are provided.
+
+=cut
+
+sub rotate {
+ my ($class, %opts) = @_;
+ my $angle;
+
+ if (defined $opts{radians}) {
+ $angle = $opts{radians};
+ }
+ elsif (defined $opts{degrees}) {
+ $angle = $opts{degrees} * 3.1415926535 / 180;
+ }
+ else {
+ $Imager::ERRSTR = "degrees or radians parameter required";
+ return undef;
+ }
+
+ if ($opts{x} && $opts{'y'}) {
+ return $class->translate(x=>-$opts{x}, 'y'=>-$opts{'y'})
+ * $class->rotate(radians=>$angle)
+ * $class->translate(x=>$opts{x}, 'y'=>$opts{'y'});
+ }
+ else {
+ my $sin = sin($angle);
+ my $cos = cos($angle);
+ return bless [ $cos, -$sin, 0,
+ $sin, $cos, 0,
+ 0, 0, 1 ], $class;
+ }
+}
+
+=item translate(x=>$dx, y=>$dy)
+
+Translates by the specify amounts.
+
+=cut
+sub translate {
+ my ($class, %opts) = @_;
+
+ if (defined $opts{x} && defined $opts{'y'}) {
+ return bless [ 1, 0, $opts{x},
+ 0, 1, $opts{'y'},
+ 0, 0, 1 ], $class;
+ }
+
+ $Imager::ERRSTR = 'x and y parameters required';
+ return undef;
+}
+
+=item shear(x=>$sx, y=>$sy)
+
+Shear by the given amounts.
+
+=cut
+sub shear {
+ my ($class, %opts) = @_;
+
+ if (defined $opts{x} || defined $opts{'y'}) {
+ return bless [ 1, $opts{x}||0, 0,
+ $opts{'y'}||0, 1, 0,
+ 0, 0, 1 ], $class;
+ }
+ $Imager::ERRSTR = 'x and y parameters required';
+ return undef;
+}
+
+=item reflect(axis=>$axis)
+
+Reflect around the given axis, either 'x' or 'y'.
+
+=item reflect(radians=>$angle)
+
+=item reflect(degrees=>$angle)
+
+Reflect around a line drawn at the given angle from the origin.
+
+=cut
+
+sub reflect {
+ my ($class, %opts) = @_;
+
+ if (defined $opts{axis}) {
+ my $result = $class->identity;
+ if ($opts{axis} eq "y") {
+ $result->[0] = -$result->[0];
+ }
+ elsif ($opts{axis} eq "x") {
+ $result->[4] = -$result->[4];
+ }
+ else {
+ $Imager::ERRSTR = 'axis must be x or y';
+ return undef;
+ }
+
+ return $result;
+ }
+ my $angle;
+ if (defined $opts{radians}) {
+ $angle = $opts{radians};
+ }
+ elsif (defined $opts{degrees}) {
+ $angle = $opts{degrees} * 3.1415926535 / 180;
+ }
+ else {
+ $Imager::ERRSTR = 'axis, degrees or radians parameter required';
+ return undef;
+ }
+
+ # fun with matrices
+ return $class->rotate(radians=>-$angle) * $class->reflect(axis=>'x')
+ * $class->rotate(radians=>$angle);
+}
+
+=item scale(x=>$xratio, y=>$yratio)
+
+Scales at the given ratios.
+
+You can also specify a center for the scaling with the cx and cy
+parameters.
+
+=cut
+
+sub scale {
+ my ($class, %opts) = @_;
+
+ if (defined $opts{x} || defined $opts{'y'}) {
+ $opts{x} = 1 unless defined $opts{x};
+ $opts{'y'} = 1 unless defined $opts{'y'};
+ if ($opts{cx} || $opts{cy}) {
+ return $class->translate(x=>-$opts{cx}, 'y'=>-$opts{cy})
+ * $class->scale(x=>$opts{x}, 'y'=>$opts{'y'})
+ * $class->translate(x=>$opts{cx}, 'y'=>$opts{cy});
+ }
+ else {
+ return bless [ $opts{x}, 0, 0,
+ 0, $opts{'y'}, 0,
+ 0, 0, 1 ], $class;
+ }
+ }
+ else {
+ $Imager::ERRSTR = 'x or y parameter required';
+ return undef;
+ }
+}
+
+=item _mult()
+
+Implements the overloaded '*' operator. Internal use.
+
+Currently both the left and right-hand sides of the operator must be
+an Imager::Matrix2d.
+
+=cut
+sub _mult {
+ my ($left, $right, $order) = @_;
+
+ if (ref($right) && UNIVERSAL::isa($right, __PACKAGE__)) {
+ if ($order) {
+ ($left, $right) = ($right, $left);
+ }
+ my @result;
+ for my $i (0..2) {
+ for my $j (0..2) {
+ my $accum = 0;
+ for my $k (0..2) {
+ $accum += $left->[3*$i + $k] * $right->[3*$k + $j];
+ }
+ $result[3*$i+$j] = $accum;
+ }
+ }
+ return bless \@result, __PACKAGE__;
+ }
+ else {
+ # presumably N * matrix or matrix * N
+ return undef; # for now
+ }
+}
+
+=item _add()
+
+Implements the overloaded binary '+' operator.
+
+Currently both the left and right sides of the operator must be
+Imager::Matrix2d objects.
+
+=cut
+sub _add {
+ my ($left, $right, $order) = @_;
+
+ if (ref($right) && UNIVERSAL::isa($right, __PACKAGE__)) {
+ my @result;
+ for (0..8) {
+ push @result, $left->[$_] + $right->[$_];
+ }
+
+ return bless \@result, __PACKAGE__;
+ }
+ else {
+ return undef;
+ }
+}
+
+=item _string()
+
+Implements the overloaded stringification operator.
+
+This returns a string containing 3 lines of text with no terminating
+newline.
+
+I tried to make it fairly nicely formatted. You might disagree :)
+
+=cut
+sub _string {
+ my ($m) = @_;
+
+ my $maxlen = 0;
+ for (@$m[0..8]) {
+ if (length() > $maxlen) {
+ $maxlen = length;
+ }
+ }
+ $maxlen <= 9 or $maxlen = 9;
+
+ my @left = ('[ ', ' ', ' ');
+ my @right = ("\n", "\n", ']');
+ my $out;
+ my $width = $maxlen+2;
+ for my $i (0..2) {
+ $out .= $left[$i];
+ for my $j (0..2) {
+ my $val = $m->[$i*3+$j];
+ if (length $val > 9) {
+ $val = sprintf("%9f", $val);
+ if ($val =~ /\./ && $val !~ /e/i) {
+ $val =~ s/0+$//;
+ $val =~ s/\.$//;
+ }
+ $val =~ s/^\s//;
+ }
+ $out .= sprintf("%-${width}s", "$val, ");
+ }
+ $out =~ s/ +\Z/ /;
+ $out .= $right[$i];
+ }
+ $out;
+}
+
+=back
+
+The following functions are shortcuts to the various constructors.
+
+These are not methods.
+
+You can import these methods with:
+
+ use Imager::Matrix2d ':handy';
+
+=over
+
+=item m2d_identity
+
+=item m2d_rotate()
+
+=item m2d_translate()
+
+=item m2d_shear()
+
+=item m2d_reflect()
+
+=back
+
+=cut
+
+sub m2d_identity {
+ return __PACKAGE__->identity;
+}
+
+sub m2d_rotate {
+ return __PACKAGE__->rotate(@_);
+}
+
+sub m2d_translate {
+ return __PACKAGE__->translate(@_);
+}
+
+sub m2d_shear {
+ return __PACKAGE__->shear(@_);
+}
+
+sub m2d_reflect {
+ return __PACKAGE__->reflect(@_);
+}
+
+sub m2d_scale {
+ return __PACKAGE__->scale(@_);
+}
+
+1;
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=head1 BUGS
+
+Needs a way to invert matrixes.
+
+=head1 SEE ALSO
+
+Imager(3), Imager::Font(3)
+
+http://www.eecs.umich.edu/~addi/perl/Imager/
+
+=cut
circleripple=>
{
type=>'rpnexpr',
+ desc=>'Adds a circular ripple effect',
rpnexpr=><<'EOS',
x y cx cy distance !dist
@dist freq / sin !scale
spiral=>
{
type=>'rpnexpr',
+ desc=>'Render a colorful spiral',
rpnexpr=><<'EOS',
x y cx cy distance !d y cy - x cx - atan2 !a
@d spacing / @a + pi 2 * % !a2
depending on content.
The list contains hashrefs, which current contain only one member,
-desc, a description of the use of the image.
+desc, a description of the use of the input image.
=item my $out = $tran->transform(\%opts, \%constants, @imgs)
--- /dev/null
+=head1 NAME
+
+Imager::interface.pod - decribes the virtual image interface
+
+=head1 SYNOPSIS
+
+
+=head1 DESCRIPTION
+
+The Imager virtual interface aims to allow image types to be created
+for special purposes, both to allow consistent access to images with
+different sample sizes, and organizations, but also to allow creation
+of synthesized or virtual images.
+
+This is a C level interface rather than Perl.
+
+=head2 Existing Images
+
+As of this writing we have the following concrete image types:
+
+=over
+
+=item *
+
+8-bit/sample direct images
+
+=item *
+
+16-bit/sample direct images
+
+=item *
+
+8-bit/sample 8-bit/index paletted images
+
+=back
+
+Currently there is only one virtual image type:
+
+=over
+
+=item *
+
+masked images, where a mask image can control write access to an
+underlying image.
+
+=back
+
+Other possible concrete images include:
+
+=over
+
+=item *
+
+"bitmaps", 1 bit/sample images (perhaps limited to a single channel)
+
+=item *
+
+16-bit/index paletted images
+
+=back
+
+Some other possible virtual images:
+
+=over
+
+=item *
+
+image alpha combining, where the combining function can be specified
+(see the layer modes in graphical editors like the GIMP or photoshop.
+
+=back
+
+=head1 THE INTERFACE
+
+Each image type needs to define a number of functions which implement
+the image operations.
+
+The image structure includes information describes the image, which
+can be used to determine the structure of the image:
+
+=over
+
+=item channels
+
+the number of samples kept for each pixel in the image. For paletted
+images the samples are kept for each entry in the palette.
+
+=item xsize, ysize
+
+the dimensions of the image in pixels.
+
+=item bytes
+
+the number of bytes of data kept for the image. Zero for virtual
+images. Does not include the space required for the palette for
+paletted images.
+
+=item ch_mask
+
+controls which samples will be written to for direct images.
+
+=item bits
+
+the number of bits kept for each sample. There are enum values
+i_8_bits, i_16_bits and i_double_bits (64).
+
+=item type
+
+the type of image, either i_direct_type or i_palette_type. Direct
+images keep the samples for every pixel image, while i_palette_type
+images keep an index into a color table for each pixel.
+
+=item virtual
+
+whether the image keeps any pixel data. If this is non-zero then
+idata points to image data, otherwise it points to implementation
+defined data, though ext_data is more likely to be used for that.
+
+=item idata
+
+image data. If the image is 8-bit direct, non-virtual, then this
+consists of each sample of the image stored one after another,
+otherwise it is implementation defined.
+
+=item tags
+
+will be used to store meta-data for an image, eg. tags from a TIFF
+file, or animation information from a GIF file. Currently unused.
+
+=item ext_data
+
+for internal use of image types. This is not released by the standard
+i_img_exorcise() function. If you create a new image type and want to
+store a pointer to allocated memory here you should point i_f_destroy
+at a function that will release the data.
+
+=back
+
+If a caller has no knowledge of the internal format of an image, the
+caller must call the appropriate image function pointer. Imager
+provides macros that wrap these functions, so it isn't necessary to
+call them directly.
+
+Many functions have a similar function with an 'f' suffix, these take
+or return samples specified with floating point values rather than
+8-bit integers (unsigned char). Floating point samples are returned
+in the range 0 to 1 inclusive.
+
+=over
+
+=item i_f_ppix(im, x, y, color)
+
+=item i_f_ppixf(im, x, y, fcolor)
+
+stores the specified color at pixel (x,y) in the image. If the pixel
+can be stored return 0, otherwise -1. An image type may choose to
+return 0 under some circumstances, eg. writing to a masked area of an
+image. The color or fcolor always contains the actual samples to be
+written, rather than a palette index.
+
+=item i_f_plin(im, l, r, y, colors)
+
+=item i_f_plinf(im, l, r, y, fcolors)
+
+stores (r-l) pixels at positions (l,y) ... (r-1, y) from the array
+specified by colors (or fcolors). Returns the number of pixels
+written to. If l is negative it will return 0. If r > im->xsize then
+only (im->xsize - l) will be written.
+
+=item i_f_gpix(im, x, y, color)
+
+=item i_f_gpixf(im, x, y, fcolor)
+
+retrieves a single pixel from position (x,y). This returns the
+samples rather than the index for paletted images.
+
+=item i_f_glin(im, l, r, y, colors)
+
+=item i_f_glinf(im, l, r, y, fcolors)
+
+retrieves (r-l) pixels from positions (l, y) through (r-1, y) into the
+array specified by colors. Returns the number of pixels retrieved.
+If l < 0 no pixels are retrieved. If r > im->xsize then pixels (l, y)
+... (im->xsize-1, y) are retrieved. Retrieves the samples rather than
+the color indexes for paletted images.
+
+=item i_f_gsamp(im, l, r, y, samples, chans, chan_count)
+
+=item i_f_gsampf(im, l, r, y, fsamples, chans, chan_count)
+
+Retrieves samples from channels specified by chans (for length
+chan_count) from pixels at positions (l,y) ... (r-1, y). If chans is
+NULL then samples from channels 0 ... chan_count-1 will be retrieved.
+Returns the number of sample retrieved (_not_ the number of channels).
+If a channel in chans is not present in the image or l < 0, returns 0.
+If r > im->xsize, then the samples from (l,y) ... (im->xsize-1, y) are
+returned.
+
+=back
+
+The following are for images where type == i_palette_type only.
+
+=over
+
+=item i_f_gpal(im, l, r, y, vals)
+
+Retrieves color indexes from the image for pixels (l, y) ... (r-1, y)
+into vals. Returns the number of indexes retrieved.
+
+=item i_f_ppal(im, l, r, y, vals)
+
+Stores color indexes into the image for pixels (l, y) ... (r-1, y)
+from vals. Returns the number of indexes retrieved. If indices are
+outside the range of the images palette, then you may have problems
+reading those pixels with i_gpix() or i_glin().
+
+=item i_f_addcolors(im, colors, count)
+
+Adds the count colors to the image's palette. Returns the index of
+the first color added, or -1 if there is not enough space for count
+colors.
+
+=item i_f_getcolors(im, index, colors, count)
+
+Retrieves count colors from the image's palette starting from entry
+index in the palette. Returns non-zero on success.
+
+=item i_f_colorcount(im)
+
+Returns the number of colors in the image's palette. Returns -1 if
+this is not a paletted image.
+
+=item i_f_maxcolors(im)
+
+Returns the maximum number of colors that can fit in the image's
+palette. Returns -1 if this is not a paletted image.
+
+=item i_f_findcolor(im, color, entry)
+
+Searches the image's palette for the specified color, setting *entry
+to the index and returning non-zero. Returns zero if the color is not
+found.
+
+=item i_f_setcolors_t(im, index, colors, count)
+
+Sets count colors starting from index in the image from the array
+colors. The colors to be set must already have entries in the image's
+palette. Returns non-zero on success.
+
+=back
+
+Finally, the i_f_destroy function pointer can be set which is called
+when the image is destroyed. This can be used to release memory
+pointed to by ext_data or release any other resources.
+
+When writing to a paletted image with i_ppix() or i_plin() and the
+color you are writing doesn't exist in the image, then it's possible
+that the image will be internally converted to a direct image with the
+same number of channels.
+
+=head1 TOOLS
+
+Several functions have been written to simplify creating new image types.
+
+These tools are available by including imagei.h.
+
+=head2 Floating point wrappers
+
+These functions implement the floating point sample versions of each
+interface function in terms of the integer sample version.
+
+These are:
+
+=over
+
+=item i_ppixf_fp
+
+=item i_gpixf_fp
+
+=item i_plinf_fp
+
+=item i_glinf_fp
+
+=item i_gsampf_fp
+
+=back
+
+
+=head2 Forwarding functions
+
+These functions are used in virtual images where the call should
+simply be forwarded to the underlying image. The underlying image is
+assumed to be the first pointer in a structure pointed at by ext_data.
+
+If this is not the case then these functions will just crash :)
+
+=over
+
+=item i_addcolors_forward
+
+=item i_getcolors_forward
+
+=item i_colorcount_forward
+
+=item i_maxcolors_forward
+
+=item i_findcolor_forward
+
+=item i_setcolors_forward
+
+=back
+
+=head2 Sample macros
+
+Imagei.h defines several macros for converting samples between
+different sizes.
+
+Each macro is of the form SampleI<size>ToI<size> where I<size> is one
+of 8, 16, or F (for floating-point samples).
+
+=over
+
+=item SampleFTo16(sample)
+
+=item Sample16ToF(sample)
+
+=item SampleFTo8(sample)
+
+=item Sample8ToF(sample)
+
+=item Sample16To8(num)
+
+=item Sample8To16(num)
+
+=back
+
+=cut
--- /dev/null
+/*
+=head1 NAME
+
+maskimg.c - implements masked images/image subsets
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=over
+=cut
+*/
+
+#include "image.h"
+#include "imagei.h"
+
+#include <stdio.h>
+/*
+=item i_img_mask_ext
+
+A pointer to this type of object is kept in the ext_data of a masked
+image.
+
+=cut
+*/
+
+typedef struct {
+ i_img *targ;
+ i_img *mask;
+ int xbase, ybase;
+ i_sample_t *samps; /* temp space */
+} i_img_mask_ext;
+
+#define MASKEXT(im) ((i_img_mask_ext *)((im)->ext_data))
+
+static void i_destroy_masked(i_img *im);
+static int i_ppix_masked(i_img *im, int x, int y, i_color *pix);
+static int i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix);
+static int i_plin_masked(i_img *im, int l, int r, int y, i_color *vals);
+static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_gpix_masked(i_img *im, int x, int y, i_color *pix);
+static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix);
+static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals);
+static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals);
+static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp,
+ int *chans, int chan_count);
+static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp,
+ int *chans, int chan_count);
+static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
+static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals);
+
+/*
+=item IIM_base_masked
+
+The basic data we copy into a masked image.
+
+=cut
+*/
+static i_img IIM_base_masked =
+{
+ 0, /* channels set */
+ 0, 0, 0, /* xsize, ysize, bytes */
+ ~0, /* ch_mask */
+ i_8_bits, /* bits */
+ i_palette_type, /* type */
+ 1, /* virtual */
+ NULL, /* idata */
+ { 0, 0, NULL }, /* tags */
+ NULL, /* ext_data */
+
+ i_ppix_masked, /* i_f_ppix */
+ i_ppixf_masked, /* i_f_ppixf */
+ i_plin_masked, /* i_f_plin */
+ i_plinf_masked, /* i_f_plinf */
+ i_gpix_masked, /* i_f_gpix */
+ i_gpixf_masked, /* i_f_gpixf */
+ i_glin_masked, /* i_f_glin */
+ i_glinf_masked, /* i_f_glinf */
+ i_gsamp_masked, /* i_f_gsamp */
+ i_gsampf_masked, /* i_f_gsampf */
+
+ i_gpal_masked, /* i_f_gpal */
+ i_ppal_masked, /* i_f_ppal */
+ i_addcolors_forward, /* i_f_addcolors */
+ i_getcolors_forward, /* i_f_getcolors */
+ i_colorcount_forward, /* i_f_colorcount */
+ i_maxcolors_forward, /* i_f_maxcolors */
+ i_findcolor_forward, /* i_f_findcolor */
+ i_setcolors_forward, /* i_f_setcolors */
+
+ i_destroy_masked, /* i_f_destroy */
+};
+
+/*
+=item i_img_masked_new(i_img *targ, i_img *mask, int xbase, int ybase, int w, int h)
+
+Create a new masked image.
+
+The image mask is optional, in which case the image is just a view of
+a rectangular portion of the image.
+
+The mask only has an effect of writing to the image, the entire view
+of the underlying image is readable.
+
+pixel access to mimg(x,y) is translated to targ(x+xbase, y+ybase), as long
+as (0 <= x < w) and (0 <= y < h).
+
+For a pixel to be writable, the pixel mask(x,y) must have non-zero in
+it's first channel. No scaling of the pixel is done, the channel
+sample is treated as boolean.
+
+=cut
+*/
+
+i_img *i_img_masked_new(i_img *targ, i_img *mask, int x, int y, int w, int h) {
+ i_img *im;
+ i_img_mask_ext *ext;
+
+ i_clear_error();
+ if (x >= targ->xsize || y >= targ->ysize) {
+ i_push_error(0, "subset outside of target image");
+ return NULL;
+ }
+ if (mask) {
+ if (w > mask->xsize)
+ w = mask->xsize;
+ if (h > mask->ysize)
+ h = mask->ysize;
+ }
+ if (x+w > targ->xsize)
+ w = targ->xsize - x;
+ if (y+h > targ->ysize)
+ h = targ->ysize - y;
+
+ im = mymalloc(sizeof(i_img));
+ memcpy(im, &IIM_base_masked, sizeof(i_img));
+ im->xsize = w;
+ im->ysize = h;
+ im->channels = targ->channels;
+ im->bits = targ->bits;
+ im->type = targ->type;
+ ext = mymalloc(sizeof(*ext));
+ ext->targ = targ;
+ ext->mask = mask;
+ ext->xbase = x;
+ ext->ybase = y;
+ ext->samps = mymalloc(sizeof(i_sample_t) * im->xsize);
+ im->ext_data = ext;
+
+ return im;
+}
+
+/*
+=item i_destroy_masked(i_img *im)
+
+The destruction handler for masked images.
+
+Releases the ext_data.
+
+Internal function.
+
+=cut
+*/
+
+static void i_destroy_masked(i_img *im) {
+ myfree(MASKEXT(im)->samps);
+ myfree(im->ext_data);
+}
+
+/*
+=item i_ppix_masked(i_img *im, int x, int y, i_color *pix)
+
+Write a pixel to a masked image.
+
+Internal function.
+
+=cut
+*/
+static int i_ppix_masked(i_img *im, int x, int y, i_color *pix) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ int result;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return -1;
+ if (ext->mask) {
+ i_sample_t samp;
+
+ if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
+ return 0; /* pretend it was good */
+ }
+ result = i_ppix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
+ im->type = ext->targ->type;
+ return result;
+}
+
+/*
+=item i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix)
+
+Write a pixel to a masked image.
+
+Internal function.
+
+=cut
+*/
+static int i_ppixf_masked(i_img *im, int x, int y, i_fcolor *pix) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ int result;
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return -1;
+ if (ext->mask) {
+ i_sample_t samp;
+
+ if (i_gsamp(ext->mask, x, x+1, y, &samp, NULL, 1) && !samp)
+ return 0; /* pretend it was good */
+ }
+ result = i_ppixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
+ im->type = ext->targ->type;
+ return result;
+}
+
+/*
+=item i_plin_masked(i_img *im, int l, int r, int y, i_color *vals)
+
+Write a row of data to a masked image.
+
+Internal function.
+
+=cut
+*/
+static int i_plin_masked(i_img *im, int l, int r, int y, i_color *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ int result;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (ext->mask) {
+ int i;
+ int simple = 0;
+ i_sample_t *samps = ext->samps;
+ int w = r - l;
+
+ i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
+ if (w < 10)
+ simple = 0;
+ else {
+ /* the idea is to make a fast scan to see how often the state
+ changes */
+ int changes = 0;
+ for (i = 0; i < w-1; ++i)
+ if (!samps[i] != !samps[i+1])
+ ++changes;
+ if (changes > w/3) /* just rough */
+ simple = 1;
+ }
+ if (simple) {
+ /* we'd be calling a usually more complicated i_plin function
+ almost as often as the usually simple i_ppix(), so just
+ do a simple scan
+ */
+ for (i = 0; i < w; ++i) {
+ if (samps[i])
+ i_ppix(ext->targ, l + i + ext->xbase, y + ext->ybase, vals + i);
+ }
+ im->type = ext->targ->type;
+ return r-l;
+ }
+ else {
+ /* the scan above indicates there should be some contiguous
+ regions, look for them and render
+ */
+ int start;
+ i = 0;
+ while (i < w) {
+ while (i < w && !samps[i])
+ ++i;
+ start = i;
+ while (i < w && samps[i])
+ ++i;
+ if (i != start)
+ i_plin(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
+ y + ext->ybase, vals + start);
+ }
+ im->type = ext->targ->type;
+ return w;
+ }
+ }
+ else {
+ int result = i_plin(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ im->type = ext->targ->type;
+ return result;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals)
+
+Write a row of data to a masked image.
+
+Internal function.
+
+=cut
+*/
+static int i_plinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (ext->mask) {
+ int i;
+ int simple = 0;
+ i_sample_t *samps = ext->samps;
+ int w = r - l;
+
+ i_gsamp(ext->mask, l, r, y, samps, NULL, 1);
+ if (w < 10)
+ simple = 0;
+ else {
+ /* the idea is to make a fast scan to see how often the state
+ changes */
+ int changes = 0;
+ for (i = 0; i < w-1; ++i)
+ if (!samps[i] != !samps[i+1])
+ ++changes;
+ if (changes > w/3) /* just rough */
+ simple = 1;
+ }
+ if (simple) {
+ /* we'd be calling a usually more complicated i_plin function
+ almost as often as the usually simple i_ppix(), so just
+ do a simple scan
+ */
+ for (i = 0; i < w; ++i) {
+ if (samps[i])
+ i_ppixf(ext->targ, l + i + ext->xbase, y + ext->ybase, vals+i);
+ }
+ im->type = ext->targ->type;
+ return r-l;
+ }
+ else {
+ /* the scan above indicates there should be some contiguous
+ regions, look for them and render
+ */
+ int start;
+ i = 0;
+ while (i < w) {
+ while (i < w && !samps[i])
+ ++i;
+ start = i;
+ while (i < w && samps[i])
+ ++i;
+ if (i != start)
+ i_plinf(ext->targ, l + start + ext->xbase, l + i + ext->xbase,
+ y + ext->ybase, vals + start);
+ }
+ im->type = ext->targ->type;
+ return w;
+ }
+ }
+ else {
+ int result = i_plinf(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ im->type = ext->targ->type;
+ return result;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gpix_masked(i_img *im, int x, int y, i_color *pix)
+
+Read a pixel from a masked image.
+
+Internal.
+
+=cut
+*/
+static int i_gpix_masked(i_img *im, int x, int y, i_color *pix) {
+ i_img_mask_ext *ext = MASKEXT(im);
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return -1;
+
+ return i_gpix(ext->targ, x + ext->xbase, y + ext->ybase, pix);
+}
+
+/*
+=item i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix)
+
+Read a pixel from a masked image.
+
+Internal.
+
+=cut
+*/
+static int i_gpixf_masked(i_img *im, int x, int y, i_fcolor *pix) {
+ i_img_mask_ext *ext = MASKEXT(im);
+
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return -1;
+
+ return i_gpixf(ext->targ, x + ext->xbase, y + ext->ybase, pix);
+}
+
+static int i_glin_masked(i_img *im, int l, int r, int y, i_color *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ return i_glin(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_glinf_masked(i_img *im, int l, int r, int y, i_fcolor *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ return i_glinf(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_gsamp_masked(i_img *im, int l, int r, int y, i_sample_t *samp,
+ int *chans, int chan_count) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ return i_gsamp(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, samp, chans, chan_count);
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_gsampf_masked(i_img *im, int l, int r, int y, i_fsample_t *samp,
+ int *chans, int chan_count) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ return i_gsampf(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, samp, chans, chan_count);
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_gpal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ return i_gpal(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ }
+ else {
+ return 0;
+ }
+}
+
+static int i_ppal_masked(i_img *im, int l, int r, int y, i_palidx *vals) {
+ i_img_mask_ext *ext = MASKEXT(im);
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (ext->mask) {
+ int i;
+ i_sample_t *samps = ext->samps;
+ int w = r - l;
+ int start;
+
+ i = 0;
+ while (i < w) {
+ while (i < w && !samps[i])
+ ++i;
+ start = i;
+ while (i < w && samps[i])
+ ++i;
+ if (i != start)
+ i_ppal(ext->targ, l+start+ext->xbase, l+i+ext->xbase,
+ y+ext->ybase, vals+start);
+ }
+ return w;
+ }
+ else {
+ return i_ppal(ext->targ, l + ext->xbase, r + ext->xbase,
+ y + ext->ybase, vals);
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
--- /dev/null
+/*
+=head1 NAME
+
+ palimg.c - implements paletted images for Imager.
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Implements paletted images using the new image interface.
+
+=over
+
+=item IIM_base_8bit_pal
+
+Basic 8-bit/sample paletted image
+
+=cut
+*/
+
+#include "image.h"
+#include "imagei.h"
+
+#define PALEXT(im) ((i_img_pal_ext*)((im)->ext_data))
+static int i_ppix_p(i_img *im, int x, int y, i_color *val);
+static int i_gpix_p(i_img *im, int x, int y, i_color *val);
+static int i_glin_p(i_img *im, int l, int r, int y, i_color *vals);
+static int i_plin_p(i_img *im, int l, int r, int y, i_color *vals);
+static int i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
+static int i_gpal_p(i_img *pm, int l, int r, int y, i_palidx *vals);
+static int i_ppal_p(i_img *pm, int l, int r, int y, i_palidx *vals);
+static int i_addcolors_p(i_img *im, i_color *color, int count);
+static int i_getcolors_p(i_img *im, int i, i_color *color, int count);
+static int i_colorcount_p(i_img *im);
+static int i_maxcolors_p(i_img *im);
+static int i_findcolor_p(i_img *im, i_color *color, i_palidx *entry);
+static int i_setcolors_p(i_img *im, int index, i_color *color, int count);
+
+static void i_destroy_p(i_img *im);
+
+static i_img IIM_base_8bit_pal =
+{
+ 0, /* channels set */
+ 0, 0, 0, /* xsize, ysize, bytes */
+ ~0, /* ch_mask */
+ i_8_bits, /* bits */
+ i_palette_type, /* type */
+ 0, /* virtual */
+ NULL, /* idata */
+ { 0, 0, NULL }, /* tags */
+ NULL, /* ext_data */
+
+ i_ppix_p, /* i_f_ppix */
+ i_ppixf_fp, /* i_f_ppixf */
+ i_plin_p, /* i_f_plin */
+ i_plinf_fp, /* i_f_plinf */
+ i_gpix_p, /* i_f_gpix */
+ i_gpixf_fp, /* i_f_gpixf */
+ i_glin_p, /* i_f_glin */
+ i_glinf_fp, /* i_f_glinf */
+ i_gsamp_p, /* i_f_gsamp */
+ i_gsampf_fp, /* i_f_gsampf */
+
+ i_gpal_p, /* i_f_gpal */
+ i_ppal_p, /* i_f_ppal */
+ i_addcolors_p, /* i_f_addcolors */
+ i_getcolors_p, /* i_f_getcolors */
+ i_colorcount_p, /* i_f_colorcount */
+ i_maxcolors_p, /* i_f_maxcolors */
+ i_findcolor_p, /* i_f_findcolor */
+ i_setcolors_p, /* i_f_setcolors */
+
+ i_destroy_p, /* i_f_destroy */
+};
+
+/*
+=item i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal)
+
+Creates a new paletted image.
+
+Currently 0 < maxpal <= 256
+
+=cut
+*/
+i_img *i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal) {
+ i_img_pal_ext *palext;
+
+ i_clear_error();
+ if (maxpal < 0 || maxpal > 256) {
+ i_push_error(0, "Maximum of 256 palette entries");
+ return NULL;
+ }
+ if (x < 1 || y < 1) {
+ i_push_error(0, "Image sizes must be positive");
+ return NULL;
+ }
+ if (channels < 1 || channels > MAXCHANNELS) {
+ i_push_errorf(0, "Channels must be postive and <= %d", MAXCHANNELS);
+ return NULL;
+ }
+
+ memcpy(im, &IIM_base_8bit_pal, sizeof(i_img));
+ palext = mymalloc(sizeof(i_img_pal_ext));
+ palext->pal = mymalloc(sizeof(i_color) * maxpal);
+ palext->count = 0;
+ palext->alloc = maxpal;
+ palext->last_found = -1;
+ im->ext_data = palext;
+ i_tags_new(&im->tags);
+ im->bytes = sizeof(i_palidx) * x * y;
+ im->idata = mymalloc(im->bytes);
+ im->channels = channels;
+ im->xsize = x;
+ im->ysize = y;
+
+ return im;
+}
+
+i_img *i_img_pal_new(int x, int y, int channels, int maxpal) {
+ i_img *im = mymalloc(sizeof(i_img));
+
+ return i_img_pal_new_low(im, x, y, channels, maxpal);
+}
+
+/*
+=item i_img_rgb_convert(i_img *targ, i_img *src)
+
+Converts paletted data in src to RGB data in targ
+
+Internal function.
+
+src must be a paletted image and targ must be an RGB image with the
+same width, height and channels.
+
+=cut
+*/
+static void i_img_rgb_convert(i_img *targ, i_img *src) {
+ i_color *row = mymalloc(sizeof(i_color) * targ->xsize);
+ int y;
+ for (y = 0; y < targ->ysize; ++y) {
+ i_glin(src, 0, src->xsize, y, row);
+ i_plin(targ, 0, src->xsize, y, row);
+ }
+ myfree(row);
+}
+
+/*
+=item i_img_to_rgb_inplace(im)
+
+Converts im from a paletted image to an RGB image.
+
+The conversion is done in place.
+
+The conversion cannot be done for virtual images.
+
+=cut
+*/
+int i_img_to_rgb_inplace(i_img *im) {
+ i_img temp;
+ i_color *pal;
+ int palsize;
+
+ if (im->virtual)
+ return 0;
+
+ if (im->type == i_direct_type)
+ return 1; /* trivial success */
+
+ i_img_empty_ch(&temp, im->xsize, im->ysize, im->channels);
+ i_img_rgb_convert(&temp, im);
+
+ /* nasty hack */
+ (im->i_f_destroy)(im);
+ myfree(im->idata);
+ *im = temp;
+
+ return 1;
+}
+
+/*
+=item i_img_to_pal(i_img *im, i_quantize *quant)
+
+Converts an RGB image to a paletted image
+
+=cut
+*/
+i_img *i_img_to_pal(i_img *src, i_quantize *quant) {
+ i_palidx *result;
+ i_img *im;
+
+ im = i_img_pal_new(src->xsize, src->ysize, src->channels, quant->mc_size);
+
+ quant_makemap(quant, &src, 1);
+ result = quant_translate(quant, src);
+
+ /* copy things over */
+ memcpy(im->idata, result, im->bytes);
+ PALEXT(im)->count = quant->mc_count;
+ memcpy(PALEXT(im)->pal, quant->mc_colors, sizeof(i_color) * quant->mc_count);
+
+ myfree(result);
+
+ return im;
+}
+
+/*
+=item i_img_to_rgb(i_img *src)
+
+=cut
+*/
+i_img *i_img_to_rgb(i_img *src) {
+ i_img *im = i_img_empty_ch(NULL, src->xsize, src->ysize, src->channels);
+ i_img_rgb_convert(im, src);
+
+ return im;
+}
+
+/*
+=item i_destroy_p(i_img *im)
+
+Destroys data related to a paletted image.
+
+=cut
+*/
+static void i_destroy_p(i_img *im) {
+ if (im) {
+ i_img_pal_ext *palext = im->ext_data;
+ if (palext) {
+ if (palext->pal)
+ myfree(palext->pal);
+ myfree(palext);
+ }
+ }
+}
+
+/*
+=item i_ppix_p(i_img *im, int x, int y, i_color *val)
+
+Write to a pixel in the image.
+
+Warning: converts the image to a RGB image if the color isn't already
+present in the image.
+
+=cut
+*/
+int i_ppix_p(i_img *im, int x, int y, i_color *val) {
+ i_palidx which;
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
+ return -1;
+ if (i_findcolor(im, val, &which)) {
+ ((i_palidx *)im->idata)[x + y * im->xsize] = which;
+ return 0;
+ }
+ else {
+ if (i_img_to_rgb_inplace(im)) {
+ return i_ppix(im, x, y, val);
+ }
+ else
+ return -1;
+ }
+}
+
+/*
+=item i_gpix(i_img *im, int x, int y, i_color *val)
+
+Retrieve a pixel, converting from a palette index to a color.
+
+=cut
+*/
+int i_gpix_p(i_img *im, int x, int y, i_color *val) {
+ i_palidx which;
+ if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
+ return -1;
+ }
+ which = ((i_palidx *)im->idata)[x + y * im->xsize];
+ if (which > PALEXT(im)->count)
+ return -1;
+ *val = PALEXT(im)->pal[which];
+
+ return 0;
+}
+
+/*
+=item i_glinp(i_img *im, int l, int r, int y, i_color *vals)
+
+Retrieve a row of pixels.
+
+=cut
+*/
+int i_glin_p(i_img *im, int l, int r, int y, i_color *vals) {
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ int palsize = PALEXT(im)->count;
+ i_color *pal = PALEXT(im)->pal;
+ i_palidx *data;
+ int count, i;
+ if (r > im->xsize)
+ r = im->xsize;
+ data = ((i_palidx *)im->idata) + l + y * im->xsize;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ i_palidx which = *data++;
+ if (which < palsize)
+ vals[i] = pal[which];
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_plin_p(i_img *im, int l, int r, int y, i_color *vals)
+
+Write a line of color data to the image.
+
+If any color value is not in the image when the image is converted to
+RGB.
+
+=cut
+*/
+int i_plin_p(i_img *im, int l, int r, int y, i_color *vals) {
+ int ch, count, i;
+ i_palidx *data;
+ i_palidx which;
+ if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ data = ((i_palidx *)im->idata) + l + y * im->xsize;
+ count = r - l;
+ for (i = 0; i < count; ++i) {
+ if (i_findcolor(im, vals+i, &which)) {
+ ((i_palidx *)data)[i] = which;
+ }
+ else {
+ if (i_img_to_rgb_inplace(im)) {
+ return i+i_plin(im, l+i, r, y, vals+i);
+ }
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps, int chans, int chan_count)
+
+=cut
+*/
+int i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps,
+ int *chans, int chan_count) {
+ int ch;
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ int palsize = PALEXT(im)->count;
+ i_color *pal = PALEXT(im)->pal;
+ i_palidx *data;
+ int count, i, w;
+ if (r > im->xsize)
+ r = im->xsize;
+ data = ((i_palidx *)im->idata) + l + y * im->xsize;
+ count = 0;
+ w = r - l;
+ if (chans) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ if (chans[ch] < 0 || chans[ch] >= im->channels) {
+ i_push_errorf(0, "No channel %d in this image", chans[ch]);
+ }
+ }
+
+ for (i = 0; i < w; ++i) {
+ i_palidx which = *data++;
+ if (which < palsize) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = pal[which].channel[chans[ch]];
+ ++count;
+ }
+ }
+ }
+ }
+ else {
+ for (i = 0; i < w; ++i) {
+ i_palidx which = *data++;
+ if (which < palsize) {
+ for (ch = 0; ch < chan_count; ++ch) {
+ *samps++ = pal[which].channel[ch];
+ ++count;
+ }
+ }
+ }
+ }
+ return count;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gpal_p(i_img *im, int l, int r, int y, i_palidx *vals)
+
+=cut
+*/
+
+int i_gpal_p(i_img *im, int l, int r, int y, i_palidx *vals) {
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ i_palidx *data;
+ int i, w;
+ if (r > im->xsize)
+ r = im->xsize;
+ data = ((i_palidx *)im->idata) + l + y * im->xsize;
+ w = r - l;
+ for (i = 0; i < w; ++i) {
+ *vals++ = *data++;
+ }
+ return i;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_ppal_p(i_img *im, int l, int r, int y, i_palidx *vals)
+
+=cut
+*/
+
+int i_ppal_p(i_img *im, int l, int r, int y, i_palidx *vals) {
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ i_palidx *data;
+ int i, w;
+ if (r > im->xsize)
+ r = im->xsize;
+ data = ((i_palidx *)im->idata) + l + y * im->xsize;
+ w = r - l;
+ for (i = 0; i < w; ++i) {
+ *data++ = *vals++;
+ }
+ return i;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_addcolors_p(i_img *im, i_color *color, int count)
+
+=cut
+*/
+int i_addcolors_p(i_img *im, i_color *color, int count) {
+ if (PALEXT(im)->count + count <= PALEXT(im)->alloc) {
+ int result = PALEXT(im)->count;
+ int index = result;
+
+ PALEXT(im)->count += count;
+ while (count) {
+ PALEXT(im)->pal[index++] = *color++;
+ --count;
+ }
+
+ return result;
+ }
+ else
+ return -1;
+}
+
+/*
+=item i_getcolors_p(i_img *im, int i, i_color *color, int count)
+
+=cut
+*/
+int i_getcolors_p(i_img *im, int i, i_color *color, int count) {
+ if (i >= 0 && i+count <= PALEXT(im)->count) {
+ while (count) {
+ *color++ = PALEXT(im)->pal[i++];
+ --count;
+ }
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static int color_eq(i_img *im, i_color *c1, i_color *c2) {
+ int ch;
+ for (ch = 0; ch < im->channels; ++ch) {
+ if (c1->channel[ch] != c2->channel[ch])
+ return 0;
+ }
+ return 1;
+}
+
+/*
+=item i_colorcount_p(i_img *im)
+
+=cut
+*/
+int i_colorcount_p(i_img *im) {
+ return PALEXT(im)->count;
+}
+
+/*
+=item i_maxcolors_p(i_img *im)
+
+=cut
+*/
+int i_maxcolors_p(i_img *im) {
+ return PALEXT(im)->alloc;
+}
+
+/*
+=item i_setcolors_p(i_img *im, int index, i_color *colors, int count)
+
+=cut
+*/
+int i_setcolors_p(i_img *im, int index, i_color *colors, int count) {
+ if (index >= 0 && count >= 1 && index + count < PALEXT(im)->count) {
+ while (count) {
+ PALEXT(im)->pal[index++] = *colors++;
+ --count;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+=item i_findcolor_p(i_img *im)
+
+=cut
+*/
+int i_findcolor_p(i_img *im, i_color *color, i_palidx *entry) {
+ if (PALEXT(im)->count) {
+ int i;
+ /* often the same color comes up several times in a row */
+ if (PALEXT(im)->last_found >= 0) {
+ if (color_eq(im, color, PALEXT(im)->pal + PALEXT(im)->last_found)) {
+ *entry = PALEXT(im)->last_found;
+ return 1;
+ }
+ }
+ for (i = 0; i < PALEXT(im)->count; ++i) {
+ if (color_eq(im, color, PALEXT(im)->pal + i)) {
+ PALEXT(im)->last_found = *entry = i;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
#define i_img_setmask(im,ch_mask) (symbol_table->i_img_setmask(im,ch_mask))
#define i_img_getmask(im) (symbol_table->i_img_getmask(im))
+/*
+Not needed? The i_gpix() macro in image.h will call the right function
+directly.
#define i_ppix(im,x,y,val) (symbol_table->i_ppix(im,x,y,val))
#define i_gpix(im,x,y,val) (symbol_table->i_gpix(im,x,y,val))
+*/
#define i_box(im, x1, y1, x2, y2,val) (symbol_table->i_box(im, x1, y1, x2, y2,val))
#define i_draw(im, x1, y1, x2, y2,val) (symbol_table->i_draw(im, x1, y1, x2, y2,val))
png_infop info_ptr;
int width,height,y;
volatile int cspace,channels;
+ double xres, yres;
+ int aspect_only, have_res;
+ double offx, offy;
+ char offunit[20] = "pixel";
mm_log((1,"i_writepng(0x%x,fd %d)\n",im,fd));
png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ have_res = 1;
+ if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
+ if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
+ ; /* nothing to do */
+ else
+ yres = xres;
+ }
+ else {
+ if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
+ xres = yres;
+ else
+ have_res = 0;
+ }
+ if (have_res) {
+ aspect_only = 0;
+ i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
+ xres /= 0.0254;
+ yres /= 0.0254;
+ png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
+ aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
+ }
+
png_write_info(png_ptr, info_ptr);
- for (y = 0; y < height; y++) png_write_row(png_ptr, (png_bytep) &(im->data[channels*width*y]));
+ if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
+ for (y = 0; y < height; y++)
+ png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y]));
+ }
+ else {
+ unsigned char *data = mymalloc(im->xsize * im->channels);
+ if (data) {
+ for (y = 0; y < height; y++) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
+ png_write_row(png_ptr, (png_bytep)data);
+ }
+ myfree(data);
+ }
+ else {
+ fclose(fp);
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ return 0;
+ }
+ }
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
png_infop info_ptr;
int width,height,y;
volatile int cspace,channels;
+ double xres, yres;
+ int aspect_only, have_res;
+ double offx, offy;
+ char offunit[20] = "pixel";
io_glue_commit_types(ig);
mm_log((1,"i_writepng(im %p ,ig %p)\n", im, ig));
png_set_IHDR(png_ptr, info_ptr, width, height, 8, cspace,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ have_res = 1;
+ if (i_tags_get_float(&im->tags, "i_xres", 0, &xres)) {
+ if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
+ ; /* nothing to do */
+ else
+ yres = xres;
+ }
+ else {
+ if (i_tags_get_float(&im->tags, "i_yres", 0, &yres))
+ xres = yres;
+ else
+ have_res = 0;
+ }
+ if (have_res) {
+ aspect_only = 0;
+ i_tags_get_int(&im->tags, "i_aspect_only", 0, &aspect_only);
+ xres /= 0.0254;
+ yres /= 0.0254;
+ png_set_pHYs(png_ptr, info_ptr, xres + 0.5, yres + 0.5,
+ aspect_only ? PNG_RESOLUTION_UNKNOWN : PNG_RESOLUTION_METER);
+ }
+
png_write_info(png_ptr, info_ptr);
- for (y = 0; y < height; y++) png_write_row(png_ptr, (png_bytep) &(im->data[channels*width*y]));
+ if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
+ for (y = 0; y < height; y++)
+ png_write_row(png_ptr, (png_bytep) &(im->idata[channels*width*y]));
+ }
+ else {
+ unsigned char *data = mymalloc(im->xsize * im->channels);
+ if (data) {
+ for (y = 0; y < height; y++) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
+ png_write_row(png_ptr, (png_bytep)data);
+ }
+ myfree(data);
+ }
+ else {
+ fclose(fp);
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ return 0;
+ }
+ }
png_write_end(png_ptr, info_ptr);
+static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr);
i_img*
i_readpng_wiol(io_glue *ig, int length) {
im = i_img_empty_ch(NULL,width,height,channels);
for (pass = 0; pass < number_passes; pass++)
- for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->data[channels*width*y]), NULL); }
+ for (y = 0; y < height; y++) { png_read_row(png_ptr,(png_bytep) &(im->idata[channels*width*y]), NULL); }
png_read_end(png_ptr, info_ptr);
+ get_png_tags(im, png_ptr, info_ptr);
+
png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
mm_log((1,"(0x%08X) <- i_readpng_scalar\n", im));
return im;
}
+
+static void get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
+ png_uint_32 xres, yres;
+ int unit_type;
+ if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) {
+ mm_log((1,"pHYs (%d, %d) %d\n", xres, yres, unit_type));
+ if (unit_type == PNG_RESOLUTION_METER) {
+ i_tags_set_float(&im->tags, "i_xres", 0, xres * 0.0254);
+ i_tags_set_float(&im->tags, "i_yres", 0, xres * 0.0254);
+ }
+ else {
+ i_tags_addn(&im->tags, "i_xres", 0, xres);
+ i_tags_addn(&im->tags, "i_yres", 0, yres);
+ i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
+ }
+ }
+}
if (im->channels==3) {
sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
-
+
if (mywrite(fd,header,strlen(header))<0) {
i_push_error(errno, "could not write ppm header");
mm_log((1,"i_writeppm: unable to write ppm header.\n"));
return(0);
}
- rc=mywrite(fd,im->data,im->bytes);
+ if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+ rc=mywrite(fd,im->idata,im->bytes);
+ }
+ else {
+ unsigned char *data = mymalloc(3 * im->xsize);
+ if (data != NULL) {
+ int y = 0;
+ int x, ch;
+ unsigned char *p;
+ static int rgb_chan[3] = { 0, 1, 2 };
+
+ rc = 0;
+ while (y < im->ysize && rc >= 0) {
+ i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
+ rc = mywrite(fd, data, im->xsize * 3);
+ }
+ myfree(data);
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ }
if (rc<0) {
i_push_error(errno, "could not write ppm data");
mm_log((1,"i_writeppm: unable to write ppm data.\n"));
mm_log((1,"i_writeppm: unable to write pgm header.\n"));
return(0);
}
-
- rc=mywrite(fd,im->data,im->bytes);
+
+ if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+ rc=mywrite(fd,im->idata,im->bytes);
+ }
+ else {
+ unsigned char *data = mymalloc(im->xsize);
+ if (data != NULL) {
+ int y = 0;
+ int x, ch;
+ int chan = 0;
+ unsigned char *p;
+
+ rc = 0;
+ while (y < im->ysize && rc >= 0) {
+ i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
+ rc = mywrite(fd, data, im->xsize);
+ }
+ myfree(data);
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ }
if (rc<0) {
i_push_error(errno, "could not write pgm data");
mm_log((1,"i_writeppm: unable to write pgm data.\n"));
io_glue_commit_types(ig);
- if (im->channels==3) {
+ if (im->channels == 3) {
sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
-
- if (ig->writecb(ig, header, strlen(header) )<0) {
+ if (ig->writecb(ig,header,strlen(header))<0) {
i_push_error(errno, "could not write ppm header");
mm_log((1,"i_writeppm: unable to write ppm header.\n"));
return(0);
}
-
- rc = ig->writecb(ig, im->data, im->bytes);
+
+ if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+ rc = ig->writecb(ig,im->idata,im->bytes);
+ }
+ else {
+ unsigned char *data = mymalloc(3 * im->xsize);
+ if (data != NULL) {
+ int y = 0;
+ int x, ch;
+ unsigned char *p;
+ static int rgb_chan[3] = { 0, 1, 2 };
+
+ rc = 0;
+ while (y < im->ysize && rc >= 0) {
+ i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
+ rc = ig->writecb(ig, data, im->xsize * 3);
+ }
+ myfree(data);
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ }
if (rc<0) {
i_push_error(errno, "could not write ppm data");
mm_log((1,"i_writeppm: unable to write ppm data.\n"));
else if (im->channels == 1) {
sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
im->xsize, im->ysize);
- if (ig->writecb(ig, header, strlen(header)) < 0) {
+ if (ig->writecb(ig,header, strlen(header)) < 0) {
i_push_error(errno, "could not write pgm header");
mm_log((1,"i_writeppm: unable to write pgm header.\n"));
return(0);
}
-
- rc = ig->writecb(ig, im->data, im->bytes);
+
+ if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+ rc=ig->writecb(ig,im->idata,im->bytes);
+ }
+ else {
+ unsigned char *data = mymalloc(im->xsize);
+ if (data != NULL) {
+ int y = 0;
+ int x, ch;
+ int chan = 0;
+ unsigned char *p;
+
+ rc = 0;
+ while (y < im->ysize && rc >= 0) {
+ i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
+ rc = ig->writecb(ig, data, im->xsize);
+ }
+ myfree(data);
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ }
if (rc<0) {
i_push_error(errno, "could not write pgm data");
mm_log((1,"i_writeppm: unable to write pgm data.\n"));
mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
return(0);
}
-
+
return(1);
}
#include <unistd.h>
#endif
#include <string.h>
+#include <errno.h>
#define TRUE 1
if (rc!=inbuflen) { fprintf(stderr,"Premature end of file.\n"); exit(2); }
interleave(inbuffer,ilbuffer,im->xsize,datachannels);
expandchannels(ilbuffer,exbuffer,im->xsize,datachannels,storechannels);
- memcpy(&(im->data[im->xsize*storechannels*k]),exbuffer,exbuflen);
+ /* FIXME? Do we ever want to save to a virtual image? */
+ memcpy(&(im->idata[im->xsize*storechannels*k]),exbuffer,exbuflen);
k++;
}
undef_int
i_writeraw_wiol(i_img* im, io_glue *ig) {
int rc;
+
io_glue_commit_types(ig);
+ i_clear_error();
mm_log((1,"writeraw(im %p,ig %p)\n", im, ig));
if (im == NULL) { mm_log((1,"Image is empty\n")); return(0); }
- rc = ig->writecb(ig, im->data, im->bytes);
- if (rc != im->bytes) {
- mm_log((1,"i_writeraw: Couldn't write to file\n"));
- return(0);
+ if (!im->virtual) {
+ rc=ig->writecb(ig,im->idata,im->bytes);
+ if (rc!=im->bytes) {
+ i_push_error(errno, "Could not write to file");
+ mm_log((1,"i_writeraw: Couldn't write to file\n"));
+ return(0);
+ }
+ }
+ else {
+ int y;
+
+ if (im->type == i_direct_type) {
+ /* just save it as 8-bits, maybe support saving higher bit count
+ raw images later */
+ int line_size = im->xsize * im->channels;
+ unsigned char *data = mymalloc(line_size);
+ if (data) {
+ int y = 0;
+ rc = line_size;
+ while (rc == line_size && y < im->ysize) {
+ i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels);
+ rc = ig->writecb(ig, data, line_size);
+ ++y;
+ }
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ if (rc != line_size) {
+ i_push_error(errno, "write error");
+ return 0;
+ }
+ }
+ else {
+ /* paletted image - assumes the caller puts the palette somewhere
+ else
+ */
+ int line_size = sizeof(i_palidx) * im->xsize;
+ i_palidx *data = mymalloc(sizeof(i_palidx) * im->xsize);
+ if (data) {
+ int y = 0;
+ rc = line_size;
+ while (rc == line_size && y < im->ysize) {
+ i_gpal(im, 0, im->xsize, y, data);
+ rc = ig->writecb(ig, data, line_size);
+ ++y;
+ }
+ myfree(data);
+ }
+ else {
+ i_push_error(0, "Out of memory");
+ return 0;
+ }
+ if (rc != line_size) {
+ i_push_error(errno, "write error");
+ return 0;
+ }
+ }
}
+
return(1);
}
--- /dev/null
+/*
+=head1 NAME
+
+ rotate.c - implements image rotations
+
+=head1 SYNOPSIS
+
+ i_img *i_rotate90(i_img *src, int degrees)
+
+=head1 DESCRIPTION
+
+Implements basic 90 degree rotations of an image.
+
+Other rotations will be added as tuits become available.
+
+=cut
+*/
+
+#include "image.h"
+#include <math.h> /* for floor() */
+
+i_img *i_rotate90(i_img *src, int degrees) {
+ i_img *targ;
+ int x, y;
+
+ i_clear_error();
+
+ if (degrees == 180) {
+ /* essentially the same as flipxy(..., 2) except that it's not
+ done in place */
+ targ = i_sametype(src, src->xsize, src->ysize);
+ if (src->type == i_direct_type) {
+ if (src->bits == i_8_bits) {
+ i_color *vals = mymalloc(src->xsize * sizeof(i_color));
+ for (y = 0; y < src->ysize; ++y) {
+ i_color tmp;
+ i_glin(src, 0, src->xsize, y, vals);
+ for (x = 0; x < src->xsize/2; ++x) {
+ tmp = vals[x];
+ vals[x] = vals[src->xsize - x - 1];
+ vals[src->xsize - x - 1] = tmp;
+ }
+ i_plin(targ, 0, src->xsize, src->ysize - y - 1, vals);
+ }
+ myfree(vals);
+ }
+ else {
+ i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor));
+ for (y = 0; y < src->ysize; ++y) {
+ i_fcolor tmp;
+ i_glinf(src, 0, src->xsize, y, vals);
+ for (x = 0; x < src->xsize/2; ++x) {
+ tmp = vals[x];
+ vals[x] = vals[src->xsize - x - 1];
+ vals[src->xsize - x - 1] = tmp;
+ }
+ i_plinf(targ, 0, src->xsize, src->ysize - y - 1, vals);
+ }
+ myfree(vals);
+ }
+ }
+ else {
+ i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx));
+
+ for (y = 0; y < src->ysize; ++y) {
+ i_palidx tmp;
+ i_gpal(src, 0, src->xsize, y, vals);
+ for (x = 0; x < src->xsize/2; ++x) {
+ tmp = vals[x];
+ vals[x] = vals[src->xsize - x - 1];
+ vals[src->xsize - x - 1] = tmp;
+ }
+ i_ppal(targ, 0, src->xsize, src->ysize - y - 1, vals);
+ }
+
+ myfree(vals);
+ }
+
+ return targ;
+ }
+ else if (degrees == 270 || degrees == 90) {
+ int tx, txstart, txinc;
+ int ty, tystart, tyinc;
+
+ if (degrees == 270) {
+ txstart = 0;
+ txinc = 1;
+ tystart = src->xsize-1;
+ tyinc = -1;
+ }
+ else {
+ txstart = src->ysize-1;
+ txinc = -1;
+ tystart = 0;
+ tyinc = 1;
+ }
+ targ = i_sametype(src, src->ysize, src->xsize);
+ if (src->type == i_direct_type) {
+ if (src->bits == i_8_bits) {
+ i_color *vals = mymalloc(src->xsize * sizeof(i_color));
+
+ tx = txstart;
+ for (y = 0; y < src->ysize; ++y) {
+ i_glin(src, 0, src->xsize, y, vals);
+ ty = tystart;
+ for (x = 0; x < src->xsize; ++x) {
+ i_ppix(targ, tx, ty, vals+x);
+ ty += tyinc;
+ }
+ tx += txinc;
+ }
+ myfree(vals);
+ }
+ else {
+ i_fcolor *vals = mymalloc(src->xsize * sizeof(i_fcolor));
+
+ tx = txstart;
+ for (y = 0; y < src->ysize; ++y) {
+ i_glinf(src, 0, src->xsize, y, vals);
+ ty = tystart;
+ for (x = 0; x < src->xsize; ++x) {
+ i_ppixf(targ, tx, ty, vals+x);
+ ty += tyinc;
+ }
+ tx += txinc;
+ }
+ myfree(vals);
+ }
+ }
+ else {
+ i_palidx *vals = mymalloc(src->xsize * sizeof(i_palidx));
+
+ tx = txstart;
+ for (y = 0; y < src->ysize; ++y) {
+ i_gpal(src, 0, src->xsize, y, vals);
+ ty = tystart;
+ for (x = 0; x < src->xsize; ++x) {
+ i_ppal(targ, tx, tx+1, ty, vals+x);
+ ty += tyinc;
+ }
+ tx += txinc;
+ }
+ myfree(vals);
+ }
+ return targ;
+ }
+ else {
+ i_push_error(0, "i_rotate90() only rotates at 90, 180, or 270 degrees");
+ return NULL;
+ }
+}
+
+/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */
+/* linear interpolation */
+static i_color interp_i_color(i_color before, i_color after, double pos,
+ int channels) {
+ i_color out;
+ int ch;
+
+ pos -= floor(pos);
+ for (ch = 0; ch < channels; ++ch)
+ out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+
+ return out;
+}
+
+/* hopefully this will be inlined (it is with -O3 with gcc 2.95.4) */
+/* linear interpolation */
+static i_fcolor interp_i_fcolor(i_fcolor before, i_fcolor after, double pos,
+ int channels) {
+ i_fcolor out;
+ int ch;
+
+ pos -= floor(pos);
+ for (ch = 0; ch < channels; ++ch)
+ out.channel[ch] = (1-pos) * before.channel[ch] + pos * after.channel[ch];
+
+ return out;
+}
+
+i_img *i_matrix_transform(i_img *src, int xsize, int ysize, double *matrix) {
+ i_img *result = i_sametype(src, xsize, ysize);
+ int x, y;
+ int ch;
+ int i, j;
+ double sx, sy, sz;
+ double out[3];
+
+ if (src->type == i_direct_type) {
+ if (src->bits == i_8_bits) {
+ i_color *vals = mymalloc(xsize * sizeof(i_color));
+ i_color black;
+
+ for (ch = 0; ch < src->channels; ++ch)
+ black.channel[ch] = 0;
+
+ for (y = 0; y < ysize; ++y) {
+ for (x = 0; x < xsize; ++x) {
+ /* dividing by sz gives us the ability to do perspective
+ transforms */
+ sz = x * matrix[6] + y * matrix[7] + matrix[8];
+ if (abs(sz) > 0.0000001) {
+ sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
+ sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
+ }
+
+ /* anything outside these ranges is either a broken co-ordinate
+ or outside the source */
+ if (abs(sz) > 0.0000001
+ && sx >= -1 && sx < src->xsize
+ && sy >= -1 && sy < src->ysize) {
+
+ if (sx != (int)sx) {
+ if (sy != (int)sy) {
+ i_color c[2][2];
+ i_color ci2[2];
+ for (i = 0; i < 2; ++i)
+ for (j = 0; j < 2; ++j)
+ if (i_gpix(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
+ c[j][i] = black;
+ for (j = 0; j < 2; ++j)
+ ci2[j] = interp_i_color(c[j][0], c[j][1], sx, src->channels);
+ vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
+ }
+ else {
+ i_color ci2[2];
+ for (i = 0; i < 2; ++i)
+ if (i_gpix(src, floor(sx)+i, sy, ci2+i))
+ ci2[i] = black;
+ vals[x] = interp_i_color(ci2[0], ci2[1], sx, src->channels);
+ }
+ }
+ else {
+ if (sy != (int)sy) {
+ i_color ci2[2];
+ for (i = 0; i < 2; ++i)
+ if (i_gpix(src, sx, floor(sy)+i, ci2+i))
+ ci2[i] = black;
+ vals[x] = interp_i_color(ci2[0], ci2[1], sy, src->channels);
+ }
+ else {
+ /* all the world's an integer */
+ i_gpix(src, sx, sy, vals+x);
+ }
+ }
+ }
+ else {
+ vals[x] = black;
+ }
+ }
+ i_plin(result, 0, xsize, y, vals);
+ }
+ myfree(vals);
+ }
+ else {
+ i_fcolor *vals = mymalloc(xsize * sizeof(i_fcolor));
+ i_fcolor black;
+
+ for (ch = 0; ch < src->channels; ++ch)
+ black.channel[ch] = 0;
+
+ for (y = 0; y < ysize; ++y) {
+ for (x = 0; x < xsize; ++x) {
+ /* dividing by sz gives us the ability to do perspective
+ transforms */
+ sz = x * matrix[6] + y * matrix[7] + matrix[8];
+ if (abs(sz) > 0.0000001) {
+ sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
+ sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
+ }
+
+ /* anything outside these ranges is either a broken co-ordinate
+ or outside the source */
+ if (abs(sz) > 0.0000001
+ && sx >= -1 && sx < src->xsize
+ && sy >= -1 && sy < src->ysize) {
+
+ if (sx != (int)sx) {
+ if (sy != (int)sy) {
+ i_fcolor c[2][2];
+ i_fcolor ci2[2];
+ for (i = 0; i < 2; ++i)
+ for (j = 0; j < 2; ++j)
+ if (i_gpixf(src, floor(sx)+i, floor(sy)+j, &c[j][i]))
+ c[j][i] = black;
+ for (j = 0; j < 2; ++j)
+ ci2[j] = interp_i_fcolor(c[j][0], c[j][1], sx, src->channels);
+ vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
+ }
+ else {
+ i_fcolor ci2[2];
+ for (i = 0; i < 2; ++i)
+ if (i_gpixf(src, floor(sx)+i, sy, ci2+i))
+ ci2[i] = black;
+ vals[x] = interp_i_fcolor(ci2[0], ci2[1], sx, src->channels);
+ }
+ }
+ else {
+ if (sy != (int)sy) {
+ i_fcolor ci2[2];
+ for (i = 0; i < 2; ++i)
+ if (i_gpixf(src, sx, floor(sy)+i, ci2+i))
+ ci2[i] = black;
+ vals[x] = interp_i_fcolor(ci2[0], ci2[1], sy, src->channels);
+ }
+ else {
+ /* all the world's an integer */
+ i_gpixf(src, sx, sy, vals+x);
+ }
+ }
+ }
+ else {
+ vals[x] = black;
+ }
+ }
+ i_plinf(result, 0, xsize, y, vals);
+ }
+ myfree(vals);
+ }
+ }
+ else {
+ /* don't interpolate for a palette based image */
+ i_palidx *vals = mymalloc(xsize * sizeof(i_palidx));
+ i_palidx black = 0;
+ i_color min;
+ int minval;
+ int ix, iy;
+
+ i_getcolors(src, 0, &min, 1);
+ minval = 0;
+ for (ch = 0; ch < src->channels; ++ch) {
+ minval += min.channel[ch];
+ }
+
+ /* find the darkest color */
+ for (i = 1; i < i_colorcount(src); ++i) {
+ i_color temp;
+ int tempval;
+ i_getcolors(src, i, &temp, 1);
+ tempval = 0;
+ for (ch = 0; ch < src->channels; ++ch) {
+ tempval += temp.channel[ch];
+ }
+ if (tempval < minval) {
+ black = i;
+ min = temp;
+ minval = tempval;
+ }
+ }
+
+ for (y = 0; y < ysize; ++y) {
+ for (x = 0; x < xsize; ++x) {
+ /* dividing by sz gives us the ability to do perspective
+ transforms */
+ sz = x * matrix[6] + y * matrix[7] + matrix[8];
+ if (abs(sz) > 0.0000001) {
+ sx = (x * matrix[0] + y * matrix[1] + matrix[2]) / sz;
+ sy = (x * matrix[3] + y * matrix[4] + matrix[5]) / sz;
+ }
+
+ /* anything outside these ranges is either a broken co-ordinate
+ or outside the source */
+ if (abs(sz) > 0.0000001
+ && sx >= -0.5 && sx < src->xsize-0.5
+ && sy >= -0.5 && sy < src->ysize-0.5) {
+
+ /* all the world's an integer */
+ ix = (int)(sx+0.5);
+ iy = (int)(sy+0.5);
+ i_gpal(src, ix, ix+1, iy, vals+x);
+ }
+ else {
+ vals[x] = black;
+ }
+ }
+ i_ppal(result, 0, xsize, y, vals);
+ }
+ myfree(vals);
+ }
+
+ return result;
+}
+
+i_matrix_mult(double *dest, double *left, double *right) {
+ int i, j, k;
+ double accum;
+
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) {
+ accum = 0.0;
+ for (k = 0; k < 3; ++k) {
+ accum += left[3*i+k] * right[3*k+j];
+ }
+ dest[3*i+j] = accum;
+ }
+ }
+}
+
+i_img *i_rotate_exact(i_img *src, double amount) {
+ double xlate1[9] = { 0 };
+ double rotate[9];
+ double xlate2[9] = { 0 };
+ double temp[9], matrix[9];
+ int x1, x2, y1, y2, newxsize, newysize;
+
+ /* first translate the centre of the image to (0,0) */
+ xlate1[0] = 1;
+ xlate1[2] = src->xsize/2.0;
+ xlate1[4] = 1;
+ xlate1[5] = src->ysize/2.0;
+ xlate1[8] = 1;
+
+ /* rotate around (0.0) */
+ rotate[0] = cos(amount);
+ rotate[1] = sin(amount);
+ rotate[2] = 0;
+ rotate[3] = -rotate[1];
+ rotate[4] = rotate[0];
+ rotate[5] = 0;
+ rotate[6] = 0;
+ rotate[7] = 0;
+ rotate[8] = 1;
+
+ x1 = ceil(abs(src->xsize * rotate[0] + src->ysize * rotate[1]));
+ x2 = ceil(abs(src->xsize * rotate[0] - src->ysize * rotate[1]));
+ y1 = ceil(abs(src->xsize * rotate[3] + src->ysize * rotate[4]));
+ y2 = ceil(abs(src->xsize * rotate[3] - src->ysize * rotate[4]));
+ newxsize = x1 > x2 ? x1 : x2;
+ newysize = y1 > y2 ? y1 : y2;
+ /* translate the centre back to the center of the image */
+ xlate2[0] = 1;
+ xlate2[2] = -newxsize/2;
+ xlate2[4] = 1;
+ xlate2[5] = -newysize/2;
+ xlate2[8] = 1;
+ i_matrix_mult(temp, xlate1, rotate);
+ i_matrix_mult(matrix, temp, xlate2);
+
+ return i_matrix_transform(src, newxsize, newysize, matrix);
+}
--- /dev/null
+#!perl -w
+# t/t01introvert.t - tests internals of image formats
+# to make sure we get expected values
+#
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+use strict;
+
+my $loaded;
+BEGIN { $| = 1; print "1..71\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:handy :all);
+$loaded = 1;
+print "ok 1\n";
+
+init_log("testout/t01introvert.log",1);
+
+my $im_g = Imager::ImgRaw::new(100, 101, 1);
+
+print Imager::i_img_getchannels($im_g) == 1
+ ? "ok 2\n" : "not ok 2 # 1 channel image channel count mismatch\n";
+print Imager::i_img_getmask($im_g) & 1
+ ? "ok 3\n" : "not ok 3 # 1 channel image bad mask\n";
+print Imager::i_img_virtual($im_g)
+ ? "not ok 4 # 1 channel image thinks it is virtual\n" : "ok 4\n";
+print Imager::i_img_bits($im_g) == 8
+ ? "ok 5\n" : "not ok 5 # 1 channel image has bits != 8\n";
+print Imager::i_img_type($im_g) == 0 # direct
+ ? "ok 6\n" : "not ok 6 # 1 channel image isn't direct\n";
+
+my @ginfo = Imager::i_img_info($im_g);
+print $ginfo[0] == 100
+ ? "ok 7\n" : "not ok 7 # 1 channel image width incorrect\n";
+print $ginfo[1] == 101
+ ? "ok 8\n" : "not ok 8 # 1 channel image height incorrect\n";
+
+undef $im_g; # can we check for release after this somehow?
+
+my $im_rgb = Imager::ImgRaw::new(100, 101, 3);
+
+print Imager::i_img_getchannels($im_rgb) == 3
+ ? "ok 9\n" : "not ok 9 # 3 channel image channel count mismatch\n";
+print +(Imager::i_img_getmask($im_rgb) & 7) == 7
+ ? "ok 10\n" : "not ok 10 # 3 channel image bad mask\n";
+print Imager::i_img_bits($im_rgb) == 8
+ ? "ok 11\n" : "not ok 11 # 3 channel image has bits != 8\n";
+print Imager::i_img_type($im_rgb) == 0 # direct
+ ? "ok 12\n" : "not ok 12 # 3 channel image isn't direct\n";
+
+undef $im_rgb;
+
+my $im_pal = Imager::i_img_pal_new(100, 101, 3, 256);
+
+print $im_pal ? "ok 13\n" : "not ok 13 # couldn't make paletted image\n";
+print Imager::i_img_getchannels($im_pal) == 3
+ ? "ok 14\n" : "not ok 14 # pal img channel count mismatch\n";
+print Imager::i_img_bits($im_pal) == 8
+ ? "ok 15\n" : "not ok 15 # pal img bits != 8\n";
+print Imager::i_img_type($im_pal) == 1
+ ? "ok 16\n" : "not ok 16 # pal img isn't paletted\n";
+
+my $red = NC(255, 0, 0);
+my $green = NC(0, 255, 0);
+my $blue = NC(0, 0, 255);
+
+my $red_idx = check_add(17, $im_pal, $red, 0);
+my $green_idx = check_add(21, $im_pal, $green, 1);
+my $blue_idx = check_add(25, $im_pal, $blue, 2);
+
+# basic writing of palette indicies
+# fill with red
+Imager::i_ppal($im_pal, 0, 0, ($red_idx) x 100) == 100
+ or print "not ";
+print "ok 29\n";
+# and blue
+Imager::i_ppal($im_pal, 50, 0, ($blue_idx) x 50) == 50
+ or print "not ";
+print "ok 30\n";
+
+# make sure we get it back
+my @pals = Imager::i_gpal($im_pal, 0, 100, 0);
+grep($_ != $red_idx, @pals[0..49]) and print "not ";
+print "ok 31\n";
+grep($_ != $blue_idx, @pals[50..99]) and print "not ";
+print "ok 32\n";
+Imager::i_gpal($im_pal, 0, 100, 0) eq "\0" x 50 . "\2" x 50 or print "not ";
+print "ok 33\n";
+my @samp = Imager::i_gsamp($im_pal, 0, 100, 0, 0, 1, 2);
+@samp == 300 or print "not ";
+print "ok 34\n";
+my @samp_exp = ((255, 0, 0) x 50, (0, 0, 255) x 50);
+my $diff = array_ncmp(\@samp, \@samp_exp);
+$diff == 0 or print "not ";
+print "ok 35\n";
+my $samp = Imager::i_gsamp($im_pal, 0, 100, 0, 0, 1, 2);
+length($samp) == 300 or print "not ";
+print "ok 36\n";
+$samp eq "\xFF\0\0" x 50 . "\0\0\xFF" x 50
+ or print "not ";
+print "ok 37\n";
+
+# reading indicies as colors
+my $c_red = Imager::i_get_pixel($im_pal, 0, 0)
+ or print "not ";
+print "ok 38\n";
+color_cmp($red, $c_red) == 0
+ or print "not ";
+print "ok 39\n";
+my $c_blue = Imager::i_get_pixel($im_pal, 50, 0)
+ or print "not ";
+print "ok 40\n";
+color_cmp($blue, $c_blue) == 0
+ or print "not ";
+print "ok 41\n";
+
+# drawing with colors
+Imager::i_ppix($im_pal, 0, 0, $green) and print "not ";
+print "ok 42\n";
+# that was in the palette, should still be paletted
+print Imager::i_img_type($im_pal) == 1
+ ? "ok 43\n" : "not ok 43 # pal img isn't paletted (but still should be)\n";
+
+my $c_green = Imager::i_get_pixel($im_pal, 0, 0)
+ or print "not ";
+print "ok 44\n";
+color_cmp($green, $c_green) == 0
+ or print "not ";
+print "ok 45\n";
+
+Imager::i_colorcount($im_pal) == 3 or print "not ";
+print "ok 46\n";
+Imager::i_findcolor($im_pal, $green) == 1 or print "not ";
+print "ok 47\n";
+
+my $black = NC(0, 0, 0);
+# this should convert the image to RGB
+Imager::i_ppix($im_pal, 1, 0, $black) and print "not ";
+print "ok 48\n";
+print Imager::i_img_type($im_pal) == 0
+ ? "ok 49\n" : "not ok 49 # pal img shouldn't be paletted now\n";
+
+my %quant =
+ (
+ colors => [$red, $green, $blue, $black],
+ makemap => 'none',
+ );
+my $im_pal2 = Imager::i_img_to_pal($im_pal, \%quant);
+$im_pal2 or print "not ";
+print "ok 50\n";
+@{$quant{colors}} == 4 or print "not ";
+print "ok 51\n";
+Imager::i_gsamp($im_pal2, 0, 100, 0, 0, 1, 2)
+ eq "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50
+ or print "not ";
+print "ok 52\n";
+
+# test the OO interfaces
+my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201)
+ or print "not ";
+print "ok 53\n";
+$impal2->getchannels == 3 or print "not ";
+print "ok 54\n";
+$impal2->bits == 8 or print "not ";
+print "ok 55\n";
+$impal2->type eq 'paletted' or print "not ";
+print "ok 56\n";
+
+{
+ my $red_idx = $impal2->addcolors(colors=>[$red])
+ or print "not ";
+ print "ok 57\n";
+ $red_idx == 0 or print "not ";
+ print "ok 58\n";
+ my $blue_idx = $impal2->addcolors(colors=>[$blue, $green])
+ or print "not ";
+ print "ok 59\n";
+ $blue_idx == 1 or print "not ";
+ print "ok 60\n";
+ my $green_idx = $blue_idx + 1;
+ my $c = $impal2->getcolors(start=>$green_idx);
+ color_cmp($green, $c) == 0 or print "not ";
+ print "ok 61\n";
+ my @cols = $impal2->getcolors;
+ @cols == 3 or print "not ";
+ print "ok 62\n";
+ my @exp = ( $red, $blue, $green );
+ for my $i (0..2) {
+ if (color_cmp($cols[$i], $exp[$i])) {
+ print "not ";
+ last;
+ }
+ }
+ print "ok 63\n";
+ $impal2->colorcount == 3 or print "not ";
+ print "ok 64\n";
+ $impal2->maxcolors == 256 or print "not ";
+ print "ok 65\n";
+ $impal2->findcolor(color=>$blue) == 1 or print "not ";
+ print "ok 66\n";
+ $impal2->setcolors(start=>0, colors=>[ $blue, $red ]) or print "not ";
+ print "ok 67\n";
+
+ # make an rgb version
+ my $imrgb2 = $impal2->to_rgb8();
+ $imrgb2->type eq 'direct' or print "not ";
+ print "ok 68\n";
+
+ # and back again, specifying the palette
+ my @colors = ( $red, $blue, $green );
+ my $impal3 = $imrgb2->to_paletted(colors=>\@colors,
+ make_colors=>'none',
+ translate=>'closest')
+ or print "not ";
+ print "ok 69\n";
+ dump_colors(@colors);
+ print "# in image\n";
+ dump_colors($impal3->getcolors);
+ $impal3->colorcount == 3 or print "not ";
+ print "ok 70\n";
+ $impal3->type eq 'paletted' or print "not ";
+ print "ok 71\n";
+}
+
+sub check_add {
+ my ($base, $im, $color, $expected) = @_;
+ my $index = Imager::i_addcolors($im, $color)
+ or print "not ";
+ print "ok ",$base++,"\n";
+ print "# $index\n";
+ $index == $expected
+ or print "not ";
+ print "ok ",$base++,"\n";
+ my ($new) = Imager::i_getcolors($im, $index)
+ or print "not ";
+ print "ok ",$base++,"\n";
+ color_cmp($new, $color) == 0
+ or print "not ";
+ print "ok ",$base++,"\n";
+
+ $index;
+}
+
+sub color_cmp {
+ my ($l, $r) = @_;
+ my @l = $l->rgba;
+ my @r = $r->rgba;
+ return $l[0] <=> $r[0]
+ || $l[1] <=> $r[1]
+ || $l[2] <=> $r[2];
+}
+
+sub array_ncmp {
+ my ($a1, $a2) = @_;
+ my $len = @$a1 < @$a2 ? @$a1 : @$a2;
+ for my $i (0..$len-1) {
+ my $diff = $a1->[$i] <=> $a2->[$i]
+ and return $diff;
+ }
+ return @$a1 <=> @$a2;
+}
+
+sub dump_colors {
+ for my $col (@_) {
+ print "# ", map(sprintf("%02X", $_), ($col->rgba)[0..2]),"\n";
+ }
+}
--- /dev/null
+#!perl -w
+
+BEGIN { $| = 1; print "1..35\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:all :handy);
+#use Data::Dumper;
+$loaded = 1;
+print "ok 1\n";
+init_log("testout/t020masked.log", 1);
+
+my $base_rgb = Imager::ImgRaw::new(100, 100, 3);
+# put something in there
+my $red = NC(255, 0, 0);
+my $green = NC(0, 255, 0);
+my $blue = NC(0, 0, 255);
+my $white = NC(255, 255, 255);
+my @cols = ($red, $green, $blue);
+for my $y (0..99) {
+ Imager::i_plin($base_rgb, 0, $y, ($cols[$y % 3] ) x 100);
+}
+
+# first a simple subset image
+my $s_rgb = Imager::i_img_masked_new($base_rgb, undef, 25, 25, 50, 50);
+
+print Imager::i_img_getchannels($s_rgb) == 3
+ ? "ok 2\n" : "not ok 2 # 1 channel image channel count mismatch\n";
+print Imager::i_img_getmask($s_rgb) & 1
+ ? "ok 3\n" : "not ok 3 # 1 channel image bad mask\n";
+print Imager::i_img_virtual($s_rgb) == 0
+ ? "not ok 4 # 1 channel image thinks it isn't virtual\n" : "ok 4\n";
+print Imager::i_img_bits($s_rgb) == 8
+ ? "ok 5\n" : "not ok 5 # 1 channel image has bits != 8\n";
+print Imager::i_img_type($s_rgb) == 0 # direct
+ ? "ok 6\n" : "not ok 6 # 1 channel image isn't direct\n";
+
+my @ginfo = i_img_info($s_rgb);
+print $ginfo[0] == 50
+ ? "ok 7\n" : "not ok 7 # image width incorrect\n";
+print $ginfo[1] == 50
+ ? "ok 8\n" : "not ok 8 # image height incorrect\n";
+
+# sample some pixels through the subset
+my $c = Imager::i_get_pixel($s_rgb, 0, 0);
+color_cmp($c, $green) == 0 or print "not ";
+print "ok 9\n";
+$c = Imager::i_get_pixel($s_rgb, 49, 49);
+# (25+49)%3 = 2
+color_cmp($c, $blue) == 0 or print "not ";
+print "ok 10\n";
+
+# try writing to it
+for my $y (0..49) {
+ Imager::i_plin($s_rgb, 0, $y, ($cols[$y % 3]) x 50);
+}
+print "ok 11\n";
+# and checking the target image
+$c = Imager::i_get_pixel($base_rgb, 25, 25);
+color_cmp($c, $red) == 0 or print "not ";
+print "ok 12\n";
+$c = Imager::i_get_pixel($base_rgb, 29, 29);
+color_cmp($c, $green) == 0 or print "not ";
+print "ok 13\n";
+
+undef $s_rgb;
+
+# a basic background
+for my $y (0..99) {
+ Imager::i_plin($base_rgb, 0, $y, ($red ) x 100);
+}
+my $mask = Imager::ImgRaw::new(50, 50, 1);
+# some venetian blinds
+for my $y (4..20) {
+ Imager::i_plin($mask, 5, $y*2, ($white) x 40);
+}
+# with a strip down the middle
+for my $y (0..49) {
+ Imager::i_plin($mask, 20, $y, ($white) x 8);
+}
+my $m_rgb = Imager::i_img_masked_new($base_rgb, $mask, 25, 25, 50, 50);
+$m_rgb or print "not ";
+print "ok 14\n";
+for my $y (0..49) {
+ Imager::i_plin($m_rgb, 0, $y, ($green) x 50);
+}
+my @color_tests =
+ (
+ [ 25+0, 25+0, $red ],
+ [ 25+19, 25+0, $red ],
+ [ 25+20, 25+0, $green ],
+ [ 25+27, 25+0, $green ],
+ [ 25+28, 25+0, $red ],
+ [ 25+49, 25+0, $red ],
+ [ 25+19, 25+7, $red ],
+ [ 25+19, 25+8, $green ],
+ [ 25+19, 25+9, $red ],
+ [ 25+0, 25+8, $red ],
+ [ 25+4, 25+8, $red ],
+ [ 25+5, 25+8, $green ],
+ [ 25+44, 25+8, $green ],
+ [ 25+45, 25+8, $red ],
+ [ 25+49, 25+49, $red ],
+ );
+my $test_num = 15;
+for my $test (@color_tests) {
+ color_test($test_num++, $base_rgb, @$test);
+}
+
+{
+ # tests for the OO versions, fairly simple, since the basic functionality
+ # is covered by the low-level interface tests
+
+ my $base = Imager->new(xsize=>100, ysize=>100)
+ or print "not ";
+ print "ok 30\n";
+ $base->box(color=>$blue, filled=>1); # fill it all
+ my $mask = Imager->new(xsize=>80, ysize=>80, channels=>1);
+ $mask->box(color=>$white, filled=>1, xmin=>5, xmax=>75, ymin=>5, ymax=>75);
+ my $m_img = $base->masked(mask=>$mask, left=>5, top=>5)
+ or print "not ";
+ print "ok 31\n";
+ $m_img->getwidth == 80 or print "not ";
+ print "ok 32\n";
+ $m_img->box(color=>$green, filled=>1);
+ color_cmp(Imager::i_get_pixel($m_img->{IMG}, 0, 0), $blue) == 0
+ or print "not ";
+ print "ok 33\n";
+ color_cmp(Imager::i_get_pixel($m_img->{IMG}, 5, 5), $green) == 0
+ or print "not ";
+ print "ok 34\n";
+
+ # older versions destroyed the Imager::ImgRaw object manually in
+ # Imager::DESTROY rather than letting Imager::ImgRaw::DESTROY
+ # destroy the object
+ # so we test here by destroying the base and mask objects and trying
+ # to draw to the masked wrapper
+ # you may need to test with ElectricFence to trigger the problem
+ undef $mask;
+ undef $base;
+ $m_img->box(color=>$blue, filled=>1);
+ print "ok 35\n";
+}
+
+sub color_test {
+ my ($num, $im, $x, $y, $expected) = @_;
+ my $c = Imager::i_get_pixel($im, $x, $y);
+ color_cmp($c, $expected) == 0 or print "not ";
+ print "ok $num # $x, $y\n";
+}
+
+sub color_cmp {
+ my ($l, $r) = @_;
+ my @l = $l->rgba;
+ my @r = $r->rgba;
+ # print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
+ return $l[0] <=> $r[0]
+ || $l[1] <=> $r[1]
+ || $l[2] <=> $r[2];
+}
+
--- /dev/null
+#!perl -w
+use strict;
+BEGIN { $| = 1; print "1..29\n"; }
+my $loaded;
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:all :handy);
+#use Data::Dumper;
+$loaded = 1;
+print "ok 1\n";
+init_log("testout/t021sixteen.t", 1);
+
+use Imager::Color::Float;
+
+my $im_g = Imager::i_img_16_new(100, 101, 1);
+
+print Imager::i_img_getchannels($im_g) == 1
+ ? "ok 2\n" : "not ok 2 # 1 channel image channel count mismatch\n";
+print Imager::i_img_getmask($im_g) & 1
+ ? "ok 3\n" : "not ok 3 # 1 channel image bad mask\n";
+print Imager::i_img_virtual($im_g)
+ ? "not ok 4 # 1 channel image thinks it is virtual\n" : "ok 4\n";
+print Imager::i_img_bits($im_g) == 16
+ ? "ok 5\n" : "not ok 5 # 1 channel image has bits != 16\n";
+print Imager::i_img_type($im_g) == 0 # direct
+ ? "ok 6\n" : "not ok 6 # 1 channel image isn't direct\n";
+
+my @ginfo = i_img_info($im_g);
+print $ginfo[0] == 100
+ ? "ok 7\n" : "not ok 7 # 1 channel image width incorrect\n";
+print $ginfo[1] == 101
+ ? "ok 8\n" : "not ok 8 # 1 channel image height incorrect\n";
+
+undef $im_g;
+
+my $im_rgb = Imager::i_img_16_new(100, 101, 3);
+
+print Imager::i_img_getchannels($im_rgb) == 3
+ ? "ok 9\n" : "not ok 9 # 3 channel image channel count mismatch\n";
+print +(Imager::i_img_getmask($im_rgb) & 7) == 7
+ ? "ok 10\n" : "not ok 10 # 3 channel image bad mask\n";
+print Imager::i_img_bits($im_rgb) == 16
+ ? "ok 11\n" : "not ok 11 # 3 channel image has bits != 16\n";
+print Imager::i_img_type($im_rgb) == 0 # direct
+ ? "ok 12\n" : "not ok 12 # 3 channel image isn't direct\n";
+
+my $redf = NCF(1, 0, 0);
+my $greenf = NCF(0, 1, 0);
+my $bluef = NCF(0, 0, 1);
+
+# fill with red
+for my $y (0..101) {
+ Imager::i_plinf($im_rgb, 0, $y, ($redf) x 100);
+}
+print "ok 13\n";
+# basic sanity
+test_colorf_gpix(14, $im_rgb, 0, 0, $redf);
+test_colorf_gpix(16, $im_rgb, 99, 0, $redf);
+test_colorf_gpix(18, $im_rgb, 0, 100, $redf);
+test_colorf_gpix(20, $im_rgb, 99, 100, $redf);
+test_colorf_glin(22, $im_rgb, 0, 0, ($redf) x 100);
+test_colorf_glin(24, $im_rgb, 0, 100, ($redf) x 100);
+
+Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
+test_colorf_glin(26, $im_rgb, 0, 1,
+ ($redf) x 20, ($greenf) x 60, ($redf) x 20);
+
+# basic OO tests
+my $oo16img = Imager->new(xsize=>200, ysize=>201, bits=>16)
+ or print "not ";
+print "ok 28\n";
+$oo16img->bits == 16 or print "not ";
+print "ok 29\n";
+
+
+sub NCF {
+ return Imager::Color::Float->new(@_);
+}
+
+sub test_colorf_gpix {
+ my ($test_base, $im, $x, $y, $expected) = @_;
+ my $c = Imager::i_gpixf($im, $x, $y);
+ $c or print "not ";
+ print "ok ",$test_base++,"\n";
+ colorf_cmp($c, $expected) == 0 or print "not ";
+ print "ok ",$test_base++,"\n";
+}
+
+sub test_colorf_glin {
+ my ($test_base, $im, $x, $y, @pels) = @_;
+
+ my @got = Imager::i_glinf($im, $x, $x+@pels, $y);
+ @got == @pels or print "not ";
+ print "ok ",$test_base++,"\n";
+ grep(colorf_cmp($pels[$_], $got[$_]), 0..$#got) and print "not ";
+ print "ok ",$test_base++,"\n";
+}
+
+sub colorf_cmp {
+ my ($c1, $c2) = @_;
+ my @s1 = map { int($_*65535.99) } $c1->rgba;
+ my @s2 = map { int($_*65535.99) } $c2->rgba;
+
+ # print "# (",join(",", @s1[0..2]),") <=> (",join(",", @s2[0..2]),")\n";
+ return $s1[0] <=> $s2[0]
+ || $s1[1] <=> $s2[1]
+ || $s1[2] <=> $s2[2];
+}
# (It may become useful if the test is moved to ./t subdirectory.)
use lib qw(blib/lib blib/arch);
-BEGIN { $| = 1; print "1..10\n"; }
+BEGIN { $| = 1; print "1..12\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager qw(:all);
i_box_filled($timg, 2, 2, 18, 18, $trans);
if (!i_has_format("png")) {
- for (2..10) {
+ for (2..12) {
print "ok $_ # skip no png support\n";
}
} else {
+ Imager::i_tags_add($img, "i_xres", 0, "300", 0);
+ Imager::i_tags_add($img, "i_yres", 0, undef, 200);
+ # the following confuses the GIMP
+ #Imager::i_tags_add($img, "i_aspect_only", 0, undef, 1);
open(FH,">testout/t102.png") || die "cannot open testout/t102.png for writing\n";
binmode(FH);
$IO = Imager::io_new_fd(fileno(FH));
print i_img_diff($img, $cmpimg)
? "not ok 4 # saved image different\n" : "ok 4\n";
+ my %tags = map { Imager::i_tags_get($img, $_) }
+ 0..Imager::i_tags_count($img) - 1;
+ abs($tags{i_xres} - 300) < 1 or print "not ";
+ print "ok 5 # i_xres: $tags{i_xres}\n";
+ abs($tags{i_yres} - 200) < 1 or print "not ";
+ print "ok 6 # i_yres: $tags{i_yres}\n";
+
open FH, "> testout/t102_trans.png"
or die "Cannot open testout/t102_trans.png: $!";
binmode FH;
$IO = Imager::io_new_fd(fileno(FH));
if (i_writepng_wiol($timg, $IO)) {
- print "ok 5\n";
+ print "ok 7\n";
}
else {
- print "ok 5 # skip - png transparency not yet implemented\n";
+ print "ok 7 # skip - png transparency not yet implemented\n";
}
close FH;
$cmpimg = i_readpng_wiol($IO, -1) || print "not ";
close(FH);
- print "ok 6\n";
+ print "ok 8\n";
print "# png average mean square pixel difference: ",sqrt(i_img_diff($timg,$cmpimg))/150*150,"\n";
print i_img_diff($timg, $cmpimg)
- ? "not ok 7 # saved image different\n" : "ok 7\n";
+ ? "not ok 9 # saved image different\n" : "ok 9\n";
# REGRESSION TEST
# png.c 1.1 would produce an incorrect image when loading images with
# 1.1 may segfault here (it does with libefence)
my $pimg = i_readpng_wiol($IO,-1)
or print "not ";
- print "ok 8\n";
+ print "ok 10\n";
close FH;
open FH, "< testimg/palette_out.png"
$IO = Imager::io_new_fd(fileno(FH));
my $poimg = i_readpng_wiol($IO, -1)
or print "not ";
- print "ok 9\n";
+ print "ok 11\n";
close FH;
if (i_img_diff($pimg, $poimg)) {
print <<EOS;
-not ok 10 # regression or you may need a more recent libpng
+not ok 12 # regression or you may need a more recent libpng
# this tests a bug in Imager's png.c v1.1
# if also tickles a bug in libpng before 1.0.5, so you may need to
# upgrade libpng
EOS
}
else {
- print "ok 10\n";
+ print "ok 12\n";
}
}
-print "1..6\n";
+#!perl -w
+print "1..15\n";
use Imager qw(:all);
-
+use strict;
init_log("testout/t103raw.log",1);
-$green=i_color_new(0,255,0,255);
-$blue=i_color_new(0,0,255,255);
-$red=i_color_new(255,0,0,255);
+my $green=i_color_new(0,255,0,255);
+my $blue=i_color_new(0,0,255,255);
+my $red=i_color_new(255,0,0,255);
-$img=Imager::ImgRaw::new(150,150,3);
-$cmpimg=Imager::ImgRaw::new(150,150,3);
+my $img=Imager::ImgRaw::new(150,150,3);
+my $cmpimg=Imager::ImgRaw::new(150,150,3);
i_box_filled($img,70,25,130,125,$green);
i_box_filled($img,20,25,80,125,$blue);
open(FH,">testout/t103.raw") || die "Cannot open testout/t103.raw for writing\n";
binmode(FH);
-$IO = Imager::io_new_fd( fileno(FH) );
+my $IO = Imager::io_new_fd( fileno(FH) );
i_writeraw_wiol($img, $IO) || die "Cannot write testout/t103.raw\n";
close(FH);
# intrl==2 is documented in raw.c but doesn't seem to be implemented
#read_test('testout/t103_img_int.raw', 4, 4, 3, 3, 2, $baseimg, 7);
+# paletted images
+my $palim = Imager::i_img_pal_new(20, 20, 3, 256)
+ or print "not ";
+print "ok 7\n";
+my $redindex = Imager::i_addcolors($palim, $red);
+my $blueindex = Imager::i_addcolors($palim, $blue);
+for my $y (0..9) {
+ Imager::i_ppal($palim, 0, $y, ($redindex) x 20);
+}
+for my $y (10..19) {
+ Imager::i_ppal($palim, 0, $y, ($blueindex) x 20);
+}
+open FH, "> testout/t103_pal.raw"
+ or die "Cannot create testout/t103_pal.raw: $!";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+i_writeraw_wiol($palim, $IO) or print "not ";
+print "ok 8\n";
+close FH;
+
+open FH, "testout/t103_pal.raw"
+ or die "Cannot open testout/t103_pal.raw: $!";
+binmode FH;
+my $data = do { local $/; <FH> };
+$data eq "\x0" x 200 . "\x1" x 200
+ or print "not ";
+print "ok 9\n";
+
+# 16-bit image
+# we don't have 16-bit reads yet
+my $img16 = Imager::i_img_16_new(150, 150, 3)
+ or print "not ";
+print "ok 10\n";
+i_box_filled($img16,70,25,130,125,$green);
+i_box_filled($img16,20,25,80,125,$blue);
+i_arc($img16,75,75,30,0,361,$red);
+i_conv($img16,[0.1, 0.2, 0.4, 0.2, 0.1]);
+
+open FH, "> testout/t103_16.raw"
+ or die "Cannot create testout/t103_16.raw: $!";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+i_writeraw_wiol($img16, $IO) or print "not ";
+print "ok 11\n";
+close FH;
+
+# try a simple virtual image
+my $maskimg = Imager::i_img_masked_new($img, undef, 0, 0, 150, 150)
+ or print "not ";
+print "ok 12\n";
+
+open FH, "> testout/t103_virt.raw"
+ or die "Cannot create testout/t103_virt.raw: $!";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+i_writeraw_wiol($maskimg, $IO) or print "not ";
+print "ok 13\n";
+close FH;
+
+open FH, "testout/t103_virt.raw"
+ or die "Cannot open testout/t103_virt.raw: $!";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+my $cmpimgmask = i_readraw_wiol($IO, 150, 150, 3, 3, 0)
+ or print "not ";
+print "ok 14\n";
+my $diff = i_img_diff($maskimg, $cmpimgmask);
+print "# difference for virtual image $diff\n";
+$diff and print "not ";
+print "ok 15\n";
+
sub read_test {
my ($in, $xsize, $ysize, $data, $store, $intrl, $base, $test) = @_;
open FH, $in or die "Cannot open $in: $!";
}
$hex =~ tr/ //d;
my $result = pack("H*", $hex);
- print unpack("H*", $result),"\n";
+ #print unpack("H*", $result),"\n";
return $result;
}
$|=1;
-print "1..26\n";
+print "1..34\n";
use Imager qw(:all);
init_log("testout/t105gif.log",1);
i_box_filled($timg, 2, 2, 18, 18, $trans);
if (!i_has_format("gif")) {
- for (1..26) { print "ok $_ # skip no gif support\n"; }
+ for (1..34) { print "ok $_ # skip no gif support\n"; }
} else {
open(FH,">testout/t105.gif") || die "Cannot open testout/t105.gif\n";
binmode(FH);
# output looks moderately horrible
open FH, ">testout/t105_mult_pall.gif" or die "Cannot create file: $!";
binmode FH;
- i_writegif_gen(fileno(FH), { make_colors=>'webmap',
- translate=>'giflib',
- gif_delays=>[ 50, 50, 50, 50 ],
- #gif_loop_count => 50,
- gif_each_palette => 1,
- }, @imgs) or print "not ";
+ if (i_writegif_gen(fileno(FH), { make_colors=>'webmap',
+ translate=>'giflib',
+ gif_delays=>[ 50, 50, 50, 50 ],
+ #gif_loop_count => 50,
+ gif_each_palette => 1,
+ }, @imgs)) {
+ print "ok 15\n";
+ }
+ else {
+ print "not ok 15 # ", join(":", map $_->[1], Imager::i_errors()),"\n";
+ }
close FH;
- print "ok 15\n";
# regression test: giflib doesn't like 1 colour images
my $img1 = Imager::ImgRaw::new(100, 100, 3);
else {
print "ok 26 # skipped\n";
}
+
+ # test reading a multi-image file into multiple images
+ open FH, "< testimg/screen2.gif"
+ or die "Cannot open testimg/screen2.gif: $!";
+ binmode FH;
+ @imgs = Imager::i_readgif_multi(fileno(FH))
+ or print "not ";
+ print "ok 27\n";
+ close FH;
+ @imgs == 2 or print "not ";
+ print "ok 28\n";
+ for my $img (@imgs) {
+ unless (Imager::i_img_type($img) == 1) {
+ print "not ";
+ last;
+ }
+ }
+ print "ok 29\n";
+ Imager::i_colorcount($imgs[0]) == 4 or print "not ";
+ print "ok 30\n";
+ Imager::i_colorcount($imgs[1]) == 2 or print "not ";
+ print "ok 31\n";
+ Imager::i_tags_find($imgs[0], "gif_left", 0) or print "not ";
+ print "ok 32\n";
+ my @tags = map {[ Imager::i_tags_get($imgs[1], $_) ]} 0..Imager::i_tags_count($imgs[1])-1;
+ my ($left) = grep $_->[0] eq 'gif_left', @tags;
+ $left && $left->[1] == 3 or print "not ";
+ print "ok 33\n";
+ # screen3.gif was saved with
+ open FH, "< testimg/screen3.gif"
+ or die "Cannot open testimg/screen3.gif: $!";
+ binmode FH;
+ @imgs = Imager::i_readgif_multi(fileno(FH))
+ or print "not ";
+ print "ok 34\n";
+ use Data::Dumper;
+ # build a big map of all tags for all images
+ @tags =
+ map {
+ my $im = $_;
+ [
+ map { join ",", map { defined() ? $_ : "undef" } Imager::i_tags_get($im, $_) }
+ 0..Imager::i_tags_count($_)-1
+ ]
+ } @imgs;
+ my $dump = Dumper(\@tags);
+ $dump =~ s/^/# /mg;
+ print "# tags from gif\n", $dump;
}
sub test_readgif_cb {
-print "1..13\n";
+#!perl -w
+print "1..20\n";
use Imager qw(:all);
$^W=1; # warnings during command-line tests
$|=1; # give us some progress in the test harness
i_box_filled($timg, 2, 2, 18, 18, $trans);
if (!i_has_format("tiff")) {
- for (1..13) {
+ for (1..20) {
print "ok $_ # skip no tiff support\n";
}
} else {
+ Imager::i_tags_add($img, "i_xres", 0, "300", 0);
+ Imager::i_tags_add($img, "i_yres", 0, undef, 250);
+ # resolutionunit is centimeters
+ Imager::i_tags_add($img, "tiff_resolutionunit", 0, undef, 3);
open(FH,">testout/t106.tiff") || die "cannot open testout/t10.tiff for writing\n";
binmode(FH);
my $IO = Imager::io_new_fd(fileno(FH));
i_img_diff($img, $cmpimg) and print "not ";
print "ok 3\n";
+ # check the tags are ok
+ my %tags = map { Imager::i_tags_get($cmpimg, $_) }
+ 0 .. Imager::i_tags_count($cmpimg) - 1;
+ abs($tags{i_xres} - 300) < 0.5 or print "not ";
+ print "ok 4\n";
+ abs($tags{i_yres} - 250) < 0.5 or print "not ";
+ print "ok 5\n";
+ $tags{tiff_resolutionunit} == 3 or print "not ";
+ print "ok 6\n";
+
$IO = Imager::io_new_bufchain();
Imager::i_writetiff_wiol($img, $IO) or die "Cannot write to bufferchain\n";
}
if ($odata eq $tiffdata) {
- print "ok 4\n";
+ print "ok 7\n";
} else {
- print "not ok 4\n";
+ print "not ok 7\n";
}
# test Micksa's tiff writer
$IO = Imager::io_new_fd(fileno(FH));
i_writetiff_wiol_faxable($faximg, $IO, 1)
or print "not ";
- print "ok 5\n";
+ print "ok 8\n";
close FH;
# test the OO interface
my $ooim = Imager->new;
$ooim->read(file=>'testout/t106.tiff')
or print "not ";
- print "ok 6\n";
+ print "ok 9\n";
$ooim->write(file=>'testout/t106_oo.tiff')
or print "not ";
- print "ok 7\n";
+ print "ok 10\n";
# OO with the fax image
my $oofim = Imager->new;
$oofim->read(file=>'testout/t106tiff_fax.tiff')
or print "not ";
- print "ok 8\n";
+ print "ok 11\n";
+
+ # this should have tags set for the resolution
+ %tags = map @$_, $oofim->tags;
+ $tags{i_xres} == 204 or print "not ";
+ print "ok 12\n";
+ $tags{i_yres} == 196 or print "not ";
+ print "ok 13\n";
+ $tags{i_aspect_only} and print "not ";
+ print "ok 14\n";
+ # resunit_inches
+ $tags{tiff_resolutionunit} == 2 or print "not ";
+ print "ok 15\n";
+
$oofim->write(file=>'testout/t106_oo_fax.tiff', class=>'fax')
or print "not ";
- print "ok 9\n";
+ print "ok 16\n";
# the following should fail since there's no type and no filename
my $oodata;
$ooim->write(data=>\$oodata)
and print "not ";
- print "ok 10\n";
+ print "ok 17\n";
# OO to data
$ooim->write(data=>\$oodata, type=>'tiff')
or print 'not ';
- print "ok 11\n";
+ print "ok 18\n";
$oodata eq $tiffdata or print "not ";
- print "ok 12\n";
+ print "ok 19\n";
# make sure we can write non-fine mode
$oofim->write(file=>'testout/t106_oo_faxlo.tiff', class=>'fax', fax_fine=>0)
or print "not ";
- print "ok 13\n";
+ print "ok 20\n";
}
# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)
-BEGIN { $| = 1; print "1..5\n"; }
+BEGIN { $| = 1; print "1..22\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager;
$loaded = 1;
$fail and print "not ";
print "ok 5\n";
+# test the new OO methods
+color_ok(6, 100, 150, 200, 255, Imager::Color->new(r=>100, g=>150, b=>200));
+color_ok(7, 101, 151, 201, 255,
+ Imager::Color->new(red=>101, green=>151, blue=>201));
+color_ok(8, 102, 255, 255, 255, Imager::Color->new(grey=>102));
+color_ok(9, 103, 255, 255, 255, Imager::Color->new(gray=>103));
+if (-e '/usr/lib/X11/rgb.txt') {
+ color_ok(10, 0, 0, 255, 255, Imager::Color->new(xname=>'blue'));
+}
+else {
+ print "ok 10 # skip - no X rgb.txt found\n";
+}
+color_ok(11, 255, 250, 250, 255,
+ Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal'));
+color_ok(12, 255, 255, 255, 255, Imager::Color->new(h=>0, 's'=>0, 'v'=>1.0));
+color_ok(13, 255, 0, 0, 255, Imager::Color->new(h=>0, 's'=>1, v=>1));
+color_ok(14, 128, 129, 130, 255, Imager::Color->new(web=>'#808182'));
+color_ok(15, 0x11, 0x22, 0x33, 255, Imager::Color->new(web=>'#123'));
+color_ok(16, 255, 150, 121, 255, Imager::Color->new(rgb=>[ 255, 150, 121 ]));
+color_ok(17, 255, 150, 121, 128,
+ Imager::Color->new(rgba=>[ 255, 150, 121, 128 ]));
+color_ok(18, 255, 0, 0, 255, Imager::Color->new(hsv=>[ 0, 1, 1 ]));
+color_ok(19, 129, 130, 131, 134,
+ Imager::Color->new(channel0=>129, channel1=>130, channel2=>131,
+ channel3=>134));
+color_ok(20, 129, 130, 131, 134,
+ Imager::Color->new(c0=>129, c1=>130, c2=>131, c3=>134));
+color_ok(21, 200, 201, 203, 204,
+ Imager::Color->new(channels=>[ 200, 201, 203, 204 ]));
+color_ok(22, 255, 250, 250, 255,
+ Imager::Color->new(name=>'snow', palette=>'testimg/test_gimp_pal'));
sub test_col {
my ($c, $r, $g, $b, $a) = @_;
+ unless ($c) {
+ print "# $Imager::ERRSTR\n";
+ return 0;
+ }
my ($cr, $cg, $cb, $ca) = $c->rgba;
return $r == $cr && $g == $cg && $b == $cb && $a == $ca;
}
+sub color_ok {
+ my ($test_num, $r, $g, $b, $a, $c) = @_;
+
+ if (test_col($c, $r, $g, $b, $a)) {
+ print "ok $test_num\n";
+ }
+ else {
+ print "not ok $test_num\n"
+ }
+}
--- /dev/null
+#!perl -w
+
+use Imager;
+my $loaded;
+BEGIN { $|=1; print "1..6\n"; }
+END { print "not ok 1\n" unless $loaded; }
+use Imager::Matrix2d ':handy';
+print "ok 1\n";
+$loaded = 1;
+
+my $id = Imager::Matrix2d->identity;
+
+almost_equal($id, [ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 ]) or print "not ";
+print "ok 2\n";
+my $trans = Imager::Matrix2d->translate(x=>10, 'y'=>-11);
+almost_equal($trans, [ 1, 0, 10,
+ 0, 1, -11,
+ 0, 0, 1 ]) or print "not ";
+print "ok 3\n";
+my $rotate = Imager::Matrix2d->rotate(degrees=>90);
+almost_equal($rotate, [ 0, -1, 0,
+ 1, 0, 0,
+ 0, 0, 1 ]) or print "not ";
+print "ok 4\n";
+
+my $shear = Imager::Matrix2d->shear(x=>0.2, 'y'=>0.3);
+almost_equal($shear, [ 1, 0.2, 0,
+ 0.3, 1, 0,
+ 0, 0, 1 ]) or print "not ";
+print "ok 5\n";
+
+my $scale = Imager::Matrix2d->scale(x=>1.2, 'y'=>0.8);
+almost_equal($scale, [ 1.2, 0, 0,
+ 0, 0.8, 0,
+ 0, 0, 1 ]) or print "not ";
+print "ok 6\n";
+
+sub almost_equal {
+ my ($m1, $m2) = @_;
+
+ for my $i (0..8) {
+ abs($m1->[$i] - $m2->[$i]) < 0.00001 or return undef;
+ }
+ return 1;
+}
$bgcolor=Imager::Color->new(255,0,0,0);
$overlay=Imager::ImgRaw::new(200,70,3);
+
+ i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1)
+ or print "not ";
+ print "ok 2\n";
- i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1);
i_draw($overlay,0,50,100,50,$bgcolor);
@bbox=i_t1_bbox(0,50.0,'XMCLH',5);
i_writeppm_wiol($overlay,$IO);
close(FH);
- print "ok 2\n";
-
$bgcolor=Imager::Color::set($bgcolor,200,200,200,0);
$backgr=Imager::ImgRaw::new(280,150,3);
i_t1_set_aa(2);
- i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1);
-
+ i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1)
+ or print "not ";
+
+ print "ok 3\n";
+
open(FH,">testout/t30t1font2.ppm") || die "cannot open testout/t35t1font.ppm\n";
binmode(FH);
$IO = Imager::io_new_fd( fileno(FH) );
i_writeppm_wiol($backgr, $IO);
close(FH);
- print "ok 3\n";
-
$rc=i_t1_destroy($fnum);
if ($fnum <0) { die "i_t1_destroy failed: rc=$rc\n"; }
--- /dev/null
+#!perl -w
+BEGIN { $| = 1; print "1..4\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:all);
+$loaded = 1;
+print "ok 1\n";
+
+init_log("testout/t37w32font.log",1);
+
+sub skip {
+ for (2..4) {
+ print "ok $_ # skip not MS Windows\n";
+ }
+ malloc_state();
+ exit(0);
+}
+
+i_has_format('w32') or skip();
+print "# has w32\n";
+
+$fontname=$ENV{'TTFONTTEST'} || 'Times New Roman Bold';
+
+# i_init_fonts(); # unnecessary for Win32 font support
+
+$bgcolor=i_color_new(255,0,0,0);
+$overlay=Imager::ImgRaw::new(200,70,3);
+
+@bbox=Imager::i_wf_bbox($fontname, 50.0,'XMCLH');
+print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
+
+Imager::i_wf_cp($fontname,$overlay,5,50,1,50.0,'XMCLH',1,1);
+i_draw($overlay,0,50,100,50,$bgcolor);
+
+open(FH,">testout/t37w32font.ppm") || die "cannot open testout/t37w32font.ppm\n";
+binmode(FH);
+i_writeppm($overlay,fileno(FH));
+close(FH);
+
+print "ok 2\n";
+
+$bgcolor=i_color_set($bgcolor,200,200,200,0);
+$backgr=Imager::ImgRaw::new(500,300,3);
+
+Imager::i_wf_text($fontname,$backgr,100,100,$bgcolor,100,'MAW.',1, 1);
+i_draw($backgr,0, 100, 499, 100, NC(0, 0, 255));
+
+open(FH,">testout/t37w32font2.ppm") || die "cannot open testout/t37w32font2.ppm\n";
+binmode(FH);
+i_writeppm($backgr,fileno(FH));
+close(FH);
+
+print "ok 3\n";
+
+my $img = Imager->new(xsize=>200, ysize=>200);
+my $font = Imager::Font->new(face=>$fontname, size=>20);
+$img->string(x=>30, y=>30, string=>"Imager", color=>NC(255, 0, 0),
+ font=>$font);
+$img->write(file=>'testout/t37_oo.ppm') or print "not ";
+print "ok 4 # ",$img->errstr||'',"\n";
--- /dev/null
+#!perl -w
+# 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 { $| = 1; print "1..10\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:all);
+$loaded = 1;
+print "ok 1\n";
+
+init_log("testout/t38ft2font.log",1);
+
+sub skip {
+ for (2..10) {
+ print "ok $_ # skip no Freetype2 library\n";
+ }
+ malloc_state();
+ exit(0);
+}
+
+if (!(i_has_format("ft2")) ) { skip(); }
+print "# has ft2\n";
+
+$fontname=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf';
+
+if (! -f $fontname) {
+ print "# cannot find fontfile for truetype test $fontname\n";
+ skip();
+}
+
+i_init_fonts();
+# i_tt_set_aa(1);
+
+$bgcolor=i_color_new(255,0,0,0);
+$overlay=Imager::ImgRaw::new(200,70,3);
+
+$ttraw=Imager::Font::FreeType2::i_ft2_new($fontname, 0);
+
+$ttraw or print Imager::_error_as_msg(),"\n";
+#use Data::Dumper;
+#warn Dumper($ttraw);
+
+@bbox=Imager::Font::FreeType2::i_ft2_bbox($ttraw, 50.0, 0, 'XMCLH');
+print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
+
+Imager::Font::FreeType2::i_ft2_cp($ttraw,$overlay,5,50,1,50.0,50, 'XMCLH',1,1, 0, 0);
+i_draw($overlay,0,50,100,50,$bgcolor);
+
+open(FH,">testout/t38ft2font.ppm") || die "cannot open testout/t38ft2font.ppm\n";
+binmode(FH);
+my $IO = Imager::io_new_fd(fileno(FH));
+i_writeppm_wiol($overlay, $IO);
+close(FH);
+
+print "ok 2\n";
+
+dotest:
+
+$bgcolor=i_color_set($bgcolor,200,200,200,0);
+$backgr=Imager::ImgRaw::new(500,300,3);
+
+# i_tt_set_aa(2);
+Imager::Font::FreeType2::i_ft2_text($ttraw,$backgr,100,150,NC(255, 64, 64),200.0,50, 'MAW',1,1,0, 0);
+Imager::Font::FreeType2::i_ft2_settransform($ttraw, [0.9659, 0.2588, 0, -0.2588, 0.9659, 0 ]);
+Imager::Font::FreeType2::i_ft2_text($ttraw,$backgr,100,150,NC(0, 128, 0),200.0,50, 'MAW',0,1, 0, 0);
+i_draw($backgr, 0,150, 499, 150, NC(0, 0, 255));
+
+open(FH,">testout/t38ft2font2.ppm") || die "cannot open testout/t38ft2font.ppm\n";
+binmode(FH);
+$IO = Imager::io_new_fd(fileno(FH));
+i_writeppm_wiol($backgr,$IO);
+close(FH);
+
+print "ok 3\n";
+
+#$fontname = 'fontfiles/arial.ttf';
+my $oof = Imager::Font->new(file=>$fontname, type=>'ft2', index=>0)
+ or print "not ";
+print "ok 4\n";
+
+my $im = Imager->new(xsize=>400, ysize=>250);
+
+$im->string(font=>$oof,
+ text=>"Via OO",
+ x=>20,
+ y=>20,
+ size=>60,
+ color=>NC(255, 128, 255),
+ aa => 1,
+ align=>0) or print "not ";
+print "ok 5\n";
+$oof->transform(matrix=>[1, 0.1, 0, 0, 1, 0])
+ or print "not ";
+print "ok 6\n";
+$im->string(font=>$oof,
+ text=>"Shear",
+ x=>20,
+ 'y'=>40,
+ size=>60,
+ sizew=>50,
+ channel=>1,
+ aa=>1,
+ align=>1) or print "not ";
+print "ok 7\n";
+use Imager::Matrix2d ':handy';
+$oof->transform(matrix=>m2d_rotate(degrees=>-30));
+#$oof->transform(matrix=>m2d_identity());
+$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)
+and
+$im->string(font=>$oof,
+ text=>"SPIN",
+ x=>20,
+ 'y'=>50,
+ size=>50,
+ sizew=>40,
+ channel=>2,
+ aa => 1,
+ align=>0, vlayout=>0) or print "not ";
+print "ok 8\n";
+
+$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
+if ($] >= 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
+ if ($im->string(font=>$oof,
+ text=>$text,
+ x=>20,
+ 'y'=>200,
+ size=>50,
+ color=>NC(0,255,0),
+ aa=>1)) {
+ print "ok 9\n";
+ }
+ else {
+ print "not ok 9 # ",$im->errstr,"\n";
+ }
+}
+else {
+ print "ok 9 # skip no native UTF8 support in this version of perl\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) = '';
+if ($im->string(font=>$oof,
+ text=>$text,
+ x=>20,
+ 'y'=>230,
+ size=>50,
+ color=>NC(255,128,0),
+ aa=>1,
+ utf8=>1)) {
+ print "ok 10\n";
+}
+else {
+ print "not ok 10 # ",$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, ) or print "not ";
+}
+
+$im->write(file=>'testout/t38_oo.ppm')
+ or print "# could not save OO output: ",$im->errstr,"\n";
-BEGIN { $| = 1; print "1..6\n"; }
+BEGIN { $| = 1; print "1..10\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager;
$im6->write(type=>'pnm', file=>'testout/t56c.ppm')
|| die "Cannot write testout/t56c.ppm";
}
+
+use Imager::Transform;
+
+# some simple tests
+my @funcs = Imager::Transform->list or print "not ";
+print "ok 7\n";
+my $tran = Imager::Transform->new($funcs[0]) or print "not ";
+print "ok 8\n";
+$tran->describe() eq Imager::Transform->describe($funcs[0]) or print "not ";
+print "ok 9\n";
+# look for a function that takes inputs (at least one does)
+my @needsinputs = grep Imager::Transform->new($_)->inputs, @funcs;
+# make sure they're
+my @inputs = Imager::Transform->new($needsinputs[0])->inputs;
+$inputs[0]{desc} or print "not ";
+print "ok 10\n";
+# at some point I might want to test the actual transformations
-BEGIN { $| = 1; print "1..5\n"; }
+BEGIN { $| = 1; print "1..41\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager;
}
else { print "ok 5\n"; }
+rot_test(6, $img, 90, 4);
+rot_test(10, $img, 180, 2);
+rot_test(14, $img, 270, 4);
+rot_test(18, $img, 0, 1);
+
+my $pimg = $img->to_paletted();
+rot_test(22, $pimg, 90, 4);
+rot_test(26, $pimg, 180, 2);
+rot_test(30, $pimg, 270, 4);
+rot_test(34, $pimg, 0, 1);
+
+my $timg = $img->rotate(right=>90)->rotate(right=>270);
+Imager::i_img_diff($img->{IMG}, $timg->{IMG}) and print "not ";
+print "ok 38\n";
+$timg = $img->rotate(right=>90)->rotate(right=>180)->rotate(right=>90);
+Imager::i_img_diff($img->{IMG}, $timg->{IMG}) and print "not ";
+print "ok 39\n";
+
+# this could use more tests
+my $rimg = $img->rotate(degrees=>10)
+ or print "not ";
+print "ok 40\n";
+if (!$rimg->write(file=>"testout/t64_rot10.ppm")) {
+ print "# Cannot save: ",$rimg->errstr,"\n";
+}
+
+my $trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1])
+ or print "not ";
+print "ok 41\n";
+$trimg->write(file=>"testout/t64_trans.ppm")
+ or print "# Cannot save: ",$trimg->errstr,"\n";
+
+sub rot_test {
+ my ($testnum, $src, $degrees, $count) = @_;
+
+ my $cimg = $src->copy();
+ my $in;
+ for (1..$count) {
+ $in = $cimg;
+ $cimg = $cimg->rotate(right=>$degrees)
+ or last;
+ }
+ if ($cimg) {
+ print "ok ",$testnum++,"\n";
+ my $diff = Imager::i_img_diff($src->{IMG}, $cimg->{IMG});
+ if ($diff) {
+ print "not ok ",$testnum++," # final image doesn't match input\n";
+ for (1..3) {
+ print "ok ",$testnum++," # skipped\n";
+ }
+ }
+ else {
+ # check that other parameters match
+ $src->type eq $cimg->type or print "not ";
+ print "ok ",$testnum++," # type\n";
+ $src->bits eq $cimg->bits or print "not ";
+ print "ok ",$testnum++," # bits\n";
+ $src->getchannels eq $cimg->getchannels or print "not ";
+ print "ok ",$testnum++," # channels \n";
+ }
+ }
+ else {
+ print "not ok ",$testnum++," # rotate returned undef:",$in->errstr,"\n";
+ for (1..4) {
+ print "ok ",$testnum++," # skipped\n";
+ }
+ }
+}
+
sub skip {
print $_[0];
print "ok 2 # skip\n";
use Imager qw(:all :handy);
-print "1..4\n";
+print "1..17\n";
my $imbase = Imager::ImgRaw::new(200,300,3);
# currently this requires visual inspection of the output files
my $im = Imager->new;
if ($im->read(file=>'testimg/scale.ppm')) {
+ print "ok 5\n";
my $out;
$out = $im->convert(preset=>'gray')
- or die "Cannot convert to gray:", $im->errstr;
- # FIXME we can't save 1 channel ppm files yet
- $out->write(file=>'testout/t67_gray.ppm', type=>'pnm')
- or print "# Cannot save testout/t67_gray.ppm:", $out->errstr;
+ or print "not ";
+ print "ok 6\n";
+ if ($out->write(file=>'testout/t67_gray.ppm', type=>'pnm')) {
+ print "ok 7\n";
+ }
+ else {
+ print "not ok 7 # Cannot save testout/t67_gray.ppm:", $out->errstr,"\n";
+ }
$out = $im->convert(preset=>'blue')
- or die "Cannot convert blue:", $im->errstr;
- # FIXME we can't save 1 channel ppm files yet
- $out->write(file=>'testout/t67_blue.ppm', type=>'pnm')
- or print "# Cannot save testout/t67_blue.ppm:", $out->errstr;
+ or print "not ";
+ print "ok 8\n";
+
+ if ($out->write(file=>'testout/t67_blue.ppm', type=>'pnm')) {
+ print "ok 9\n";
+ }
+ else {
+ print "not ok 9 # Cannot save testout/t67_blue.ppm:", $out->errstr, "\n";
+ }
+}
+else {
+ print "not ok 5 # could not load testout/scale.ppm\n";
+ print map "ok $_ # skipped\n", 6..9;
+}
+
+# test against 16-bit/sample images
+my $im16targ = Imager::i_img_16_new(200, 300, 3);
+unless (i_convert($im16targ, $imbase, [ [ 0, 0, 0, 1 ],
+ [ 0, 0, 0, 0 ],
+ [ 0, 0, 0, 0 ] ])) {
+ print "not ok 10 # call failed\n";
+ print map "ok $_ # skipped\n", 11..12;
+}
+else {
+ print "ok 10\n";
+
+ # image should still be 16-bit
+ Imager::i_img_bits($im16targ) == 16
+ or print "not ";
+ print "ok 11\n";
+ # make sure that it's roughly red
+ my $c = Imager::i_gpixf($im16targ, 0, 0);
+ my @ch = $c->rgba;
+ abs($ch[0] - 1) <= 0.0001 && abs($ch[1]) <= 0.0001 && abs($ch[2]) <= 0.0001
+ or print "not ";
+ print "ok 12\n";
+}
+
+# test against palette based images
+my $impal = Imager::i_img_pal_new(200, 300, 3, 256);
+my $black = NC(0, 0, 0);
+my $blackindex = Imager::i_addcolors($impal, $black)
+ or print "not ";
+print "ok 13\n";
+for my $y (0..299) {
+ Imager::i_ppal($impal, 0, $y, ($black) x 200);
+}
+my $impalout = Imager::i_img_pal_new(200, 300, 3, 256);
+if (i_convert($impalout, $impal, [ [ 0, 0, 0, 0 ],
+ [ 0, 0, 0, 1 ],
+ [ 0, 0, 0, 0 ] ])) {
+ Imager::i_img_type($impalout) == 1 or print "not ";
+ print "ok 14\n";
+ Imager::i_colorcount($impalout) == 1 or print "not ";
+ print "ok 15\n";
+ my $c = Imager::i_getcolors($impalout, $blackindex) or print "not ";
+ print "ok 16\n";
+ my @ch = $c->rgba;
+ print "# @ch\n";
+ $ch[0] == 0 && $ch[1] == 255 && $ch[2] == 0
+ or print "not ";
+ print "ok 17\n";
}
else {
- die "could not load testout/scale.ppm\n";
+ print "not ok 14 # could not convert paletted image\n";
+ print map "ok $_ # skipped\n", 15..17;
}
--- /dev/null
+#!perl -w
+
+BEGIN { $| = 1; print "1..23\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Imager qw(:all :handy);
+$loaded = 1;
+print "ok 1\n";
+init_log("testout/t69rubthru.log", 1);
+
+# raw interface
+my $targ = Imager::ImgRaw::new(100, 100, 3);
+my $src = Imager::ImgRaw::new(80, 80, 4);
+my $halfred = NC(255, 0, 0, 128);
+i_box_filled($src, 20, 20, 60, 60, $halfred);
+i_rubthru($targ, $src, 10, 10) or print "not ";
+print "ok 2\n";
+my $c = Imager::i_get_pixel($targ, 10, 10) or print "not ";
+print "ok 3\n";
+color_cmp($c, NC(0, 0, 0)) == 0 or print "not ";
+print "ok 4\n";
+$c = Imager::i_get_pixel($targ, 30, 30) or print "not ";
+print "ok 5\n";
+color_cmp($c, NC(128, 0, 0)) == 0 or print "not ";
+print "ok 6\n";
+
+my $black = NC(0, 0, 0);
+# reset the target and try a grey+alpha source
+i_box_filled($targ, 0, 0, 100, 100, $black);
+my $gsrc = Imager::ImgRaw::new(80, 80, 2);
+my $halfwhite = NC(255, 128, 0);
+i_box_filled($gsrc, 20, 20, 60, 60, $halfwhite);
+i_rubthru($targ, $gsrc, 10, 10) or print "not ";
+print "ok 7\n";
+$c = Imager::i_get_pixel($targ, 15, 15) or print "not ";
+print "ok 8\n";
+color_cmp($c, NC(0, 0, 0)) == 0 or print "not ";
+print "ok 9\n";
+$c = Imager::i_get_pixel($targ, 30, 30) or print "not ";
+print "ok 10\n";
+color_cmp($c, NC(128, 128, 128)) == 0 or print "not ";
+print "ok 11\n";
+
+# try grey target and grey alpha source
+my $gtarg = Imager::ImgRaw::new(100, 100, 1);
+i_rubthru($gtarg, $gsrc, 10, 10) or print "not ";
+print "ok 12\n";
+$c = Imager::i_get_pixel($gtarg, 10, 10) or print "not ";
+print "ok 13\n";
+($c->rgba)[0] == 0 or print "not ";
+print "ok 14\n";
+(Imager::i_get_pixel($gtarg, 30, 30)->rgba)[0] == 128 or print "not ";
+print "ok 15\n";
+
+# an attempt rub a 4 channel image over 1 channel should fail
+i_rubthru($gtarg, $src, 10, 10) and print "not ";
+print "ok 16\n";
+
+# simple test for 16-bit/sample images
+my $targ16 = Imager::i_img_16_new(100, 100, 3);
+i_rubthru($targ16, $src, 10, 10) or print "not ";
+print "ok 17\n";
+$c = Imager::i_get_pixel($targ16, 30, 30) or print "not ";
+print "ok 18\n";
+color_cmp($c, NC(128, 0, 0)) == 0 or print "not ";
+print "ok 19\n";
+
+# check the OO interface
+my $ootarg = Imager->new(xsize=>100, ysize=>100);
+my $oosrc = Imager->new(xsize=>80, ysize=>80, channels=>4);
+$oosrc->box(color=>$halfred, xmin=>20, ymin=>20, xmax=>60, ymax=>60,
+ filled=>1);
+$ootarg->rubthrough(src=>$oosrc, tx=>10, ty=>10) or print "not ";
+print "ok 20\n";
+color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 10, 10), NC(0, 0, 0)) == 0
+ or print "not ";
+print "ok 21\n";
+color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 30, 30), NC(128, 0, 0)) == 0
+ or print "not ";
+print "ok 22\n";
+
+# make sure we fail as expected
+my $oogtarg = Imager->new(xsize=>100, ysize=>100, channels=>1);
+$oogtarg->rubthrough(src=>$oosrc) and print "not ";
+print "ok 23\n";
+
+
+sub color_cmp {
+ my ($l, $r) = @_;
+ my @l = $l->rgba;
+ my @r = $r->rgba;
+ print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
+ return $l[0] <=> $r[0]
+ || $l[1] <=> $r[1]
+ || $l[2] <=> $r[2];
+}
+
+
+#!perl -w
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl test.pl'
# (It may become useful if the test is moved to ./t subdirectory.)
-BEGIN { $| = 1; print "1..8\n"; }
+BEGIN { $| = 1; print "1..24\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager qw(:all :handy);
print "not ok 4 # ",$im2->errstr,"\n";
print "ok 5 # skipped - couldn't load image\n";
}
+
+ # test the read_multi interface
+ my @imgs = Imager->read_multi();
+ @imgs and print "not ";
+ print "ok 9\n";
+ Imager->errstr =~ /type parameter/ or print "not ";
+ print "ok 10 # ",Imager->errstr,"\n";
+
+ @imgs = Imager->read_multi(type=>'gif');
+ @imgs and print "not ";
+ print "ok 11\n";
+ Imager->errstr =~ /file/ or print "not ";
+ print "ok 12 # ",Imager->errstr,"\n";
+ # kill warning
+ *NONESUCH = \20;
+ @imgs = Imager->read_multi(type=>'gif', fh=>*NONESUCH);
+ @imgs and print "not ";
+ print "ok 13\n";
+ Imager->errstr =~ /fh option not open/ or print "not ";
+ print "ok 14 # ",Imager->errstr,"\n";
+ @imgs = Imager->read_multi(type=>'gif', file=>'testimg/screen2.gif');
+ @imgs == 2 or print "not ";
+ print "ok 15\n";
+ grep(!UNIVERSAL::isa($_, 'Imager'), @imgs) and print "not ";
+ print "ok 16\n";
+ grep($_->type eq 'direct', @imgs) and print "not ";
+ print "ok 17\n";
+ (my @left = $imgs[0]->tags(name=>'gif_left')) == 1 or print "not ";
+ print "ok 18\n";
+ my $left = $imgs[1]->tags(name=>'gif_left') or print "not ";
+ print "ok 19\n";
+ $left == 3 or print "not ";
+ print "ok 20\n";
+ if (Imager::i_giflib_version() >= 4.0) {
+ open FH, "< testimg/screen2.gif"
+ or die "Cannot open testimg/screen2.gif: $!";
+ binmode FH;
+ my $cb =
+ sub {
+ my $tmp;
+ read(FH, $tmp, $_[0]) and $tmp
+ };
+ @imgs = Imager->read_multi(type=>'gif',
+ callback => $cb) or print "not ";
+ print "ok 21\n";
+ close FH;
+ @imgs == 2 or print "not ";
+ print "ok 22\n";
+
+ open FH, "< testimg/screen2.gif"
+ or die "Cannot open testimg/screen2.gif: $!";
+ binmode FH;
+ my $data = do { local $/; <FH>; };
+ close FH;
+ @imgs = Imager->read_multi(type=>'gif',
+ data=>$data) or print "not ";
+ print "ok 23\n";
+ @imgs = 2 or print "not ";
+ print "ok 24\n";
+ }
+ else {
+ for (21..24) {
+ print "ok $_ # skipped - giflib3 doesn't support callbacks\n";
+ }
+ }
}
else {
- for (3..8) {
+ for (3..24) {
print "ok $_ # skipped: no gif support\n";
}
}
--- /dev/null
+/*
+=head1 NAME
+
+tags.c - functions for manipulating an images tags list
+
+=head1 SYNOPSIS
+
+ i_img_tags tags;
+ i_tags_new(&tags);
+ i_tags_destroy(&tags);
+ i_tags_addn(&tags, "name", code, idata);
+ i_tags_add(&tags, "name", code, data, data_size, idata);
+ if (i_tags_find(&tags, name, start, &entry)) { found }
+ if (i_tags_findn(&tags, code, start, &entry)) { found }
+ i_tags_delete(&tags, index);
+ count = i_tags_delbyname(tags, name);
+ count = i_tags_delbycode(tags, code);
+
+=head1 DESCRIPTION
+
+Provides functions which give write access to the tags list of an image.
+
+For read access directly access the fields (do not write any fields
+directly).
+
+A tag is represented by an i_img_tag structure:
+
+ typedef enum {
+ itt_double,
+ iit_text
+ } i_tag_type;
+
+ typedef struct {
+ char *name; // name of a given tag, might be NULL
+ int code; // number of a given tag, -1 if it has no meaning
+ char *data; // value of a given tag if it's not an int, may be NULL
+ int size; // size of the data
+ int idata; // value of a given tag if data is NULL
+ } i_img_tag;
+
+
+=over
+
+=cut
+*/
+
+#include "image.h"
+#include <string.h>
+#include <stdlib.h>
+
+/* useful for debugging */
+void i_tags_print(i_img_tags *tags);
+
+/*
+=item i_tags_new(i_img_tags *tags)
+
+Initialize a tags structure. Should not be used if the tags structure
+has been previously used.
+
+To destroy the contents use i_tags_destroy()
+
+=cut
+*/
+
+void i_tags_new(i_img_tags *tags) {
+ tags->count = tags->alloc = 0;
+ tags->tags = NULL;
+}
+
+/*
+=item i_tags_addn(i_img_tags *tags, char *name, int code, int idata)
+
+Adds a tag that has an integer value. A simple wrapper around i_tags_add().
+
+Duplicate tags can be added.
+
+Returns non-zero on success.
+
+=cut
+*/
+
+int i_tags_addn(i_img_tags *tags, char *name, int code, int idata) {
+ return i_tags_add(tags, name, code, NULL, 0, idata);
+}
+
+/*
+=item i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size, i_tag_type type, int idata)
+
+Adds a tag to the tags list.
+
+Duplicate tags can be added.
+
+Returns non-zero on success.
+
+=cut
+*/
+
+int i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size,
+ int idata) {
+ i_img_tag work = {0};
+ if (tags->tags == NULL) {
+ int alloc = 10;
+ tags->tags = malloc(sizeof(i_img_tag) * alloc);
+ if (!tags->tags)
+ return 0;
+ tags->alloc = alloc;
+ }
+ else if (tags->count == tags->alloc) {
+ int newalloc = tags->alloc + 10;
+ void *newtags = realloc(tags->tags, sizeof(i_img_tag) * newalloc);
+ if (!newtags) {
+ return 0;
+ }
+ tags->tags = newtags;
+ tags->alloc = newalloc;
+ }
+ if (name) {
+ work.name = malloc(strlen(name)+1);
+ if (!work.name)
+ return 0;
+ strcpy(work.name, name);
+ }
+ if (data) {
+ work.data = malloc(size+1);
+ if (!work.data) {
+ if (work.name) free(work.name);
+ return 0;
+ }
+ memcpy(work.data, data, size);
+ work.data[size] = '\0'; /* convenience */
+ work.size = size;
+ }
+ work.code = code;
+ work.idata = idata;
+ tags->tags[tags->count++] = work;
+
+ return 1;
+}
+
+void i_tags_destroy(i_img_tags *tags) {
+ if (tags->tags) {
+ int i;
+ for (i = 0; i < tags->count; ++i) {
+ if (tags->tags[i].name)
+ free(tags->tags[i].name);
+ if (tags->tags[i].data)
+ free(tags->tags[i].data);
+ }
+ free(tags->tags);
+ }
+}
+
+int i_tags_find(i_img_tags *tags, char *name, int start, int *entry) {
+ if (tags->tags) {
+ while (start < tags->count) {
+ if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
+ *entry = start;
+ return 1;
+ }
+ ++start;
+ }
+ }
+ return 0;
+}
+
+int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
+ if (tags->tags) {
+ while (start < tags->count) {
+ if (tags->tags[start].code == code) {
+ *entry = start;
+ return 1;
+ }
+ ++start;
+ }
+ }
+ return 0;
+}
+
+int i_tags_delete(i_img_tags *tags, int entry) {
+ if (tags->tags && entry >= 0 && entry < tags->count) {
+ i_img_tag old = tags->tags[entry];
+ memmove(tags->tags+entry, tags->tags+entry+1,
+ tags->count-entry-1);
+ if (old.name)
+ free(old.name);
+ if (old.data)
+ free(old.data);
+ --tags->count;
+ return 1;
+ }
+ return 0;
+}
+
+int i_tags_delbyname(i_img_tags *tags, char *name) {
+ int count = 0;
+ int i;
+ if (tags->tags) {
+ for (i = tags->count-1; i >= 0; --i) {
+ if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
+ ++count;
+ i_tags_delete(tags, i);
+ }
+ }
+ }
+ return count;
+}
+
+int i_tags_delbycode(i_img_tags *tags, int code) {
+ int count = 0;
+ int i;
+ if (tags->tags) {
+ for (i = tags->count-1; i >= 0; --i) {
+ if (tags->tags[i].code == code) {
+ ++count;
+ i_tags_delete(tags, i);
+ }
+ }
+ }
+ return count;
+}
+
+int i_tags_get_float(i_img_tags *tags, char *name, int code, double *value) {
+ int index;
+ i_img_tag *entry;
+
+ if (name) {
+ if (!i_tags_find(tags, name, 0, &index))
+ return 0;
+ }
+ else {
+ if (!i_tags_findn(tags, code, 0, &index))
+ return 0;
+ }
+ entry = tags->tags+index;
+ if (entry->data)
+ *value = atof(entry->data);
+ else
+ *value = entry->idata;
+
+ return 1;
+}
+
+int i_tags_set_float(i_img_tags *tags, char *name, int code, double value) {
+ char temp[40];
+
+ sprintf(temp, "%.30g", value);
+ if (name)
+ i_tags_delbyname(tags, name);
+ else
+ i_tags_delbycode(tags, code);
+
+ return i_tags_add(tags, name, code, temp, strlen(temp), 0);
+}
+
+int i_tags_get_int(i_img_tags *tags, char *name, int code, int *value) {
+ int index;
+ i_img_tag *entry;
+
+ if (name) {
+ if (!i_tags_find(tags, name, 0, &index))
+ return 0;
+ }
+ else {
+ if (!i_tags_findn(tags, code, 0, &index))
+ return 0;
+ }
+ entry = tags->tags+index;
+ if (entry->data)
+ *value = atoi(entry->data);
+ else
+ *value = entry->idata;
+
+ return 1;
+}
+
+int i_tags_get_string(i_img_tags *tags, char *name, int code,
+ char *value, size_t value_size) {
+ int index;
+ i_img_tag *entry;
+
+ if (name) {
+ if (!i_tags_find(tags, name, 0, &index))
+ return 0;
+ }
+ else {
+ if (!i_tags_findn(tags, code, 0, &index))
+ return 0;
+ }
+ entry = tags->tags+index;
+ if (entry->data) {
+ size_t cpsize = value_size < entry->size ? value_size : entry->size;
+ memcpy(value, entry->data, cpsize);
+ if (cpsize == value_size)
+ --cpsize;
+ value[cpsize] = '\0';
+ }
+ else {
+ sprintf(value, "%d", entry->data);
+ }
+
+ return 1;
+}
+
+void i_tags_print(i_img_tags *tags) {
+ int i;
+ printf("Alloc %d\n", tags->alloc);
+ printf("Count %d\n", tags->count);
+ for (i = 0; i < tags->count; ++i) {
+ i_img_tag *tag = tags->tags + i;
+ printf("Tag %d\n", i);
+ if (tag->name)
+ printf(" Name : %s\n", tag->name);
+ printf(" Code : %d\n", tag->code);
+ if (tag->data) {
+ int pos;
+ printf(" Data : %d => '", tag->size);
+ for (pos = 0; pos < tag->size; ++pos) {
+ if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
+ putchar('\\');
+ putchar(tag->data[pos]);
+ }
+ else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
+ printf("\\x%02X", tag->data[pos]);
+ else
+ putchar(tag->data[pos]);
+ }
+ printf("'\n");
+ printf(" Idata: %d\n", tag->idata);
+ }
+ }
+}
--- /dev/null
+GIMP Palette
+# test palette
+255 250 250 snow (255 250 250)
#include "tiffio.h"
#include "iolayer.h"
-
-
/*
=head1 NAME
=cut
*/
-
-
-
-
/*
=item comp_seek(h, o, w)
uint32* raster;
int tiled, error;
TIFF* tif;
+ float xres, yres;
+ uint16 resunit;
+ int gotXres, gotYres;
error = 0;
mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
im = i_img_empty_ch(NULL, width, height, channels);
+
+ if (!TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit))
+ resunit = RESUNIT_INCH;
+ gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
+ gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
+ if (gotXres || gotYres) {
+ if (!gotXres)
+ xres = yres;
+ else if (!gotYres)
+ yres = xres;
+ if (resunit == RESUNIT_CENTIMETER) {
+ /* from dots per cm to dpi */
+ xres *= 2.54;
+ yres *= 2.54;
+ }
+ i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
+ if (resunit == RESUNIT_NONE)
+ i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
+ i_tags_set_float(&im->tags, "i_xres", 0, xres);
+ i_tags_set_float(&im->tags, "i_yres", 0, yres);
+ }
/* TIFFPrintDirectory(tif, stdout, 0); good for debugging */
i_color val;
uint16 photometric;
uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
- double resolution = -1;
unsigned char *linebuf = NULL;
uint32 y;
tsize_t linebytes;
int ch, ci, rc;
uint32 x;
TIFF* tif;
+ int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
+ double xres, yres;
char *cc = mymalloc( 123 );
myfree(cc);
mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
- if (resolution > 0) {
- if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Xresolution=%d\n", resolution)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Yresolution=%d\n", resolution)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
+ got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
+ got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
+ if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
+ aspect_only = 0;
+ if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit))
+ resunit = RESUNIT_INCH;
+ if (got_xres || got_yres) {
+ if (!got_xres)
+ xres = yres;
+ else if (!got_yres)
+ yres = xres;
+ if (aspect_only) {
+ resunit = RESUNIT_NONE;
+ }
+ else {
+ if (resunit == RESUNIT_CENTIMETER) {
+ xres /= 2.54;
+ yres /= 2.54;
+ }
+ else {
+ resunit = RESUNIT_INCH;
+ }
+ }
+ if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
+ TIFFClose(tif);
+ i_img_destroy(im);
+ i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
+ TIFFClose(tif);
+ i_img_destroy(im);
+ i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
+ TIFFClose(tif);
+ i_img_destroy(im);
+ i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
+ return 0;
}
}
int rc;
uint32 x;
TIFF* tif;
- int luma_channel;
+ int luma_mask;
uint32 rowsperstrip;
float vres = fine ? 196 : 98;
+ int luma_chan;
width = im->xsize;
height = im->ysize;
switch (im->channels) {
case 1:
- luma_channel = 0;
+ case 2:
+ luma_chan = 0;
break;
case 3:
- luma_channel = 1;
+ case 4:
+ luma_chan = 1;
break;
default:
/* This means a colorspace we don't handle yet */
for(x=0; x<width; x+=8) {
int bits;
int bitpos;
+ i_sample_t luma[8];
uint8 bitval = 128;
linebuf[linebufpos]=0;
bits = width-x; if(bits>8) bits=8;
+ i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
for(bitpos=0;bitpos<bits;bitpos++) {
- int luma;
- luma = im->data[(x+bitpos+y*im->xsize)*im->channels+luma_channel];
- linebuf[linebufpos] |= ((luma>=128)?bitval:0);
+ linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
bitval >>= 1;
}
linebufpos++;
#i_img * T_PTR_NULL
Imager::Color T_PTROBJ
+Imager::Color::Float T_PTROBJ
Imager::ImgRaw T_PTROBJ
Imager::TTHandle T_PTROBJ
Imager::IO T_PTROBJ
+Imager::Font::FT2 T_PTROBJ
const char * T_PV
float T_FLOAT
float* T_ARRAY
--- /dev/null
+#include "image.h"
+#define STRICT
+#include <windows.h>
+
+/*
+=head1 NAME
+
+win32.c - implements some win32 specific code, specifically Win32 font support.
+
+=head1 SYNOPSIS
+
+ int bbox[6];
+ if (i_wf_bbox(facename, size, text, text_len, bbox)) {
+ // we have the bbox
+ }
+
+*/
+
+static void set_logfont(char *face, int size, LOGFONT *lf) {
+ memset(lf, 0, sizeof(LOGFONT));
+
+ lf->lfHeight = -size; /* character height rather than cell height */
+ lf->lfCharSet = ANSI_CHARSET;
+ lf->lfOutPrecision = OUT_TT_PRECIS;
+ lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ lf->lfQuality = PROOF_QUALITY;
+ strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
+ /* NUL terminated by the memset at the top */
+}
+
+#define fixed(x) ((x).value + ((x).fract) / 65536.0)
+
+int i_wf_bbox(char *face, int size, char *text, int length, int *bbox) {
+ LOGFONT lf;
+ HFONT font, oldFont;
+ HDC dc;
+ SIZE sz;
+ TEXTMETRIC tm;
+ ABC first, last;
+ GLYPHMETRICS gm;
+ int i;
+ MAT2 mat;
+
+ set_logfont(face, size, &lf);
+ font = CreateFontIndirect(&lf);
+ if (!font)
+ return 0;
+ dc = GetDC(NULL);
+ oldFont = (HFONT)SelectObject(dc, font);
+
+#if 1
+ if (!GetTextExtentPoint32(dc, text, length, &sz)
+ || !GetTextMetrics(dc, &tm)) {
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ DeleteObject(font);
+ return 0;
+ }
+ /* if there's a way to get a characters ascent/descent reliably, I can't
+ see it. GetGlyphOutline() seems to return the same size for
+ all characters.
+ */
+ bbox[1] = bbox[4] = tm.tmDescent;
+ bbox[2] = sz.cx;
+ bbox[3] = bbox[5] = tm.tmAscent;
+
+ if (GetCharABCWidths(dc, text[0], text[0], &first)
+ && GetCharABCWidths(dc, text[length-1], text[length-1], &last)) {
+ bbox[0] = first.abcA;
+ if (last.abcC < 0)
+ bbox[2] -= last.abcC;
+ }
+ else {
+ bbox[0] = 0;
+ }
+#else
+ for (i = 0; i < length; ++i) {
+ memset(&gm, 0, sizeof(gm));
+ memset(&mat, 0, sizeof(mat));
+ mat.eM11.value = 1;
+ mat.eM22.value = 1;
+ if (GetGlyphOutline(dc, GGO_METRICS, text[i], &gm, 0, NULL, &mat) != GDI_ERROR) {
+ printf("%02X: black (%d, %d) origin (%d, %d) cell(%d, %d)\n",
+ text[i], gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
+ gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY);
+ printf(" : mat [ %-8f %-8f ]\n", fixed(mat.eM11), fixed(mat.eM12));
+ printf(" [ %-8f %-8f ]\n", fixed(mat.eM21), fixed(mat.eM22));
+ }
+ else {
+ printf("Could not get metrics for '\\x%02X'\n", text[i]);
+ }
+ if (GetCharABCWidths(dc, text[i], text[i], &first)) {
+ printf("%02X: %d %d %d\n", text[i], first.abcA, first.abcB, first.abcC);
+ }
+ }
+#endif
+
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ DeleteObject(font);
+
+ return 1;
+}
+
+
+/* renders the text to an in-memory RGB bitmap
+ It would be nice to render to greyscale, but Windows doesn't have
+ native greyscale bitmaps.
+ */
+LPVOID render_text(char *face, int size, char *text, int length, int aa,
+ HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm) {
+ BITMAPINFO bmi;
+ BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
+ HDC dc, bmpDc;
+ LOGFONT lf;
+ HFONT font, oldFont;
+ SIZE sz;
+ HBITMAP bm, oldBm;
+ LPVOID bits;
+
+ dc = GetDC(NULL);
+ set_logfont(face, size, &lf);
+
+#ifdef ANTIALIASED_QUALITY
+ /* See KB article Q197076
+ "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
+ */
+ lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
+#endif
+
+ bmpDc = CreateCompatibleDC(dc);
+ if (bmpDc) {
+ font = CreateFontIndirect(&lf);
+ if (font) {
+ oldFont = SelectObject(bmpDc, font);
+ GetTextExtentPoint32(bmpDc, text, length, &sz);
+ GetTextMetrics(bmpDc, tm);
+
+ memset(&bmi, 0, sizeof(bmi));
+ bmih->biSize = sizeof(*bmih);
+ bmih->biWidth = sz.cx;
+ bmih->biHeight = sz.cy;
+ bmih->biPlanes = 1;
+ bmih->biBitCount = 24;
+ bmih->biCompression = BI_RGB;
+ bmih->biSizeImage = 0;
+ bmih->biXPelsPerMeter = 72 / 2.54 * 100;
+ bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
+ bmih->biClrUsed = 0;
+ bmih->biClrImportant = 0;
+
+ bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+
+ if (bm) {
+ oldBm = SelectObject(bmpDc, bm);
+ SetTextColor(bmpDc, RGB(255, 255, 255));
+ SetBkColor(bmpDc, RGB(0, 0, 0));
+ TextOut(bmpDc, 0, 0, text, length);
+ SelectObject(bmpDc, oldBm);
+ }
+ else {
+ i_push_errorf(0, "Could not create DIB section for render: %ld",
+ GetLastError());
+ SelectObject(bmpDc, oldFont);
+ DeleteObject(font);
+ DeleteDC(bmpDc);
+ ReleaseDC(NULL, dc);
+ return NULL;
+ }
+ SelectObject(bmpDc, oldFont);
+ DeleteObject(font);
+ }
+ else {
+ i_push_errorf(0, "Could not create logical font: %ld",
+ GetLastError());
+ DeleteDC(bmpDc);
+ ReleaseDC(NULL, dc);
+ return NULL;
+ }
+ DeleteDC(bmpDc);
+ }
+ else {
+ i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
+ ReleaseDC(NULL, dc);
+ return NULL;
+ }
+
+ ReleaseDC(NULL, dc);
+
+ *pbm = bm;
+ *psz = sz;
+
+ return bits;
+}
+
+int
+i_wf_text(char *face, i_img *im, int tx, int ty, i_color *cl, int size,
+ char *text, int len, int align, int aa) {
+ unsigned char *bits;
+ HBITMAP bm;
+ SIZE sz;
+ int line_width;
+ int x, y;
+ int ch;
+ TEXTMETRIC tm;
+ int top;
+
+ bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
+ if (!bits)
+ return 0;
+
+ line_width = sz.cx * 3;
+ line_width = (line_width + 3) / 4 * 4;
+ top = ty;
+ if (align)
+ top -= tm.tmAscent;
+
+ for (y = 0; y < sz.cy; ++y) {
+ for (x = 0; x < sz.cx; ++x) {
+ i_color pel;
+ int scale = bits[3 * x];
+ i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
+ for (ch = 0; ch < im->channels; ++ch) {
+ pel.channel[ch] =
+ ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255.0;
+ }
+ i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
+ }
+ bits += line_width;
+ }
+ DeleteObject(bm);
+
+ return 1;
+}
+
+int
+i_wf_cp(char *face, i_img *im, int tx, int ty, int channel, int size,
+ char *text, int len, int align, int aa) {
+ unsigned char *bits;
+ HBITMAP bm;
+ SIZE sz;
+ int line_width;
+ int x, y;
+ int ch;
+ TEXTMETRIC tm;
+ int top;
+
+ bits = render_text(face, size, text, len, aa, &bm, &sz, &tm);
+ if (!bits)
+ return 0;
+
+ line_width = sz.cx * 3;
+ line_width = (line_width + 3) / 4 * 4;
+ top = ty;
+ if (align)
+ top -= tm.tmAscent;
+
+ for (y = 0; y < sz.cy; ++y) {
+ for (x = 0; x < sz.cx; ++x) {
+ i_color pel;
+ int scale = bits[3 * x];
+ i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
+ pel.channel[channel] = scale;
+ i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
+ }
+ bits += line_width;
+ }
+ DeleteObject(bm);
+
+ return 1;
+}