Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

merge write to gif tags updates

  • Loading branch information...
commit 97c4effc5cb6c972260ba40636d3885fe1fcbbd6 1 parent b6cfd21
Tony Cook authored
View
4 Changes
@@ -600,6 +600,10 @@ Revision history for Perl extension Imager.
- added getpixel() and setpixel() methods
- added Artur's OSX dlload() emulation, with minor changes
- modified _color() to work around a 5.6.0 bug
+ - replaced old gif options with tags
+ - we now log which memory block is being freed before giving
+ an error on it being re-freed
+ - fixed stupid bug in deleting tags
=================================================================
View
143 Imager.pm
@@ -1,7 +1,7 @@
package Imager;
use strict;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS %formats $DEBUG %filters %DSOs $ERRSTR $fontstate %OPCODES $I2P $FORMATGUESS $warn_obsolete);
use IO::File;
use Imager::Color;
@@ -363,6 +363,8 @@ BEGIN {
};
$FORMATGUESS=\&def_guess_type;
+
+ $warn_obsolete = 1;
}
#
@@ -385,6 +387,9 @@ sub init {
if ($parms{'log'}) {
init_log($parms{'log'},$parms{'loglevel'});
}
+ if (exists $parms{'warn_obsolete'}) {
+ $warn_obsolete = $parms{'warn_obsolete'};
+ }
# if ($parms{T1LIB_CONFIG}) { $ENV{T1LIB_CONFIG}=$parms{T1LIB_CONFIG}; }
# if ( $ENV{T1LIB_CONFIG} and ( $fontstate eq 'missing conf' )) {
@@ -859,6 +864,22 @@ sub deltag {
}
}
+sub settag {
+ my ($self, %opts) = @_;
+
+ if ($opts{name}) {
+ $self->deltag(name=>$opts{name});
+ return $self->addtag(name=>$opts{name}, value=>$opts{value});
+ }
+ elsif (defined $opts{code}) {
+ $self->deltag(code=>$opts{code});
+ return $self->addtag(code=>$opts{code}, value=>$opts{value});
+ }
+ else {
+ return undef;
+ }
+}
+
my @needseekcb = qw/tiff/;
my %needseekcb = map { $_, $_ } @needseekcb;
@@ -1167,6 +1188,94 @@ sub read {
return $self;
}
+sub _fix_gif_positions {
+ my ($opts, $opt, $msg, @imgs) = @_;
+
+ my $positions = $opts->{'gif_positions'};
+ my $index = 0;
+ for my $pos (@$positions) {
+ my ($x, $y) = @$pos;
+ my $img = $imgs[$index++];
+ $img->settag(gif_left=>$x);
+ $img->settag(gif_top=>$y) if defined $y;
+ }
+ $$msg .= "replaced with the gif_left and gif_top tags";
+}
+
+my %obsolete_opts =
+ (
+ gif_each_palette=>'gif_local_map',
+ interlace => 'gif_interlace',
+ gif_delays => 'gif_delay',
+ gif_positions => \&_fix_gif_positions,
+ gif_loop_count => 'gif_loop',
+ );
+
+sub _set_opts {
+ my ($self, $opts, $prefix, @imgs) = @_;
+
+ for my $opt (keys %$opts) {
+ my $tagname = $opt;
+ if ($obsolete_opts{$opt}) {
+ my $new = $obsolete_opts{$opt};
+ my $msg = "Obsolete option $opt ";
+ if (ref $new) {
+ $new->($opts, $opt, \$msg, @imgs);
+ }
+ else {
+ $msg .= "replaced with the $new tag ";
+ $tagname = $new;
+ }
+ $msg .= "line ".(caller(2))[2]." of file ".(caller(2))[1];
+ warn $msg if $warn_obsolete && $^W;
+ }
+ next unless $tagname =~ /^\Q$prefix/;
+ my $value = $opts->{$opt};
+ if (ref $value) {
+ if (UNIVERSAL::isa($value, "Imager::Color")) {
+ my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
+ for my $img (@imgs) {
+ $img->settag(name=>$tagname, value=>$tag);
+ }
+ }
+ elsif (ref($value) eq 'ARRAY') {
+ for my $i (0..$#$value) {
+ my $val = $value->[$i];
+ if (ref $val) {
+ if (UNIVERSAL::isa($val, "Imager::Color")) {
+ my $tag = sprintf("color(%d,%d,%d,%d)", $value->rgba);
+ $i < @imgs and
+ $imgs[$i]->settag(name=>$tagname, value=>$tag);
+ }
+ else {
+ $self->_set_error("Unknown reference type " . ref($value) .
+ " supplied in array for $opt");
+ return;
+ }
+ }
+ else {
+ $i < @imgs
+ and $imgs[$i]->settag(name=>$tagname, value=>$val);
+ }
+ }
+ }
+ else {
+ $self->_set_error("Unknown reference type " . ref($value) .
+ " supplied for $opt");
+ return;
+ }
+ }
+ else {
+ # set it as a tag for every image
+ for my $img (@imgs) {
+ $img->settag(name=>$tagname, value=>$value);
+ }
+ }
+ }
+
+ return 1;
+}
+
# Write an image to file
sub write {
my $self = shift;
@@ -1180,6 +1289,9 @@ sub write {
fax_fine=>1, @_);
my $rc;
+ $self->_set_opts(\%input, "i_", $self)
+ or return undef;
+
my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1,
gif=>1 ); # this will be SO MUCH BETTER once they are all in there
@@ -1202,6 +1314,11 @@ sub write {
if ($iolready{$input{'type'}}) {
if ($input{'type'} eq 'tiff') {
+ $self->_set_opts(\%input, "tiff_", $self)
+ or return undef;
+ $self->_set_opts(\%input, "exif_", $self)
+ or return undef;
+
if (defined $input{class} && $input{class} eq 'fax') {
if (!i_writetiff_wiol_faxable($self->{IMG}, $IO, $input{fax_fine})) {
$self->{ERRSTR}='Could not write to buffer';
@@ -1214,36 +1331,50 @@ sub write {
}
}
} elsif ( $input{'type'} eq 'pnm' ) {
+ $self->_set_opts(\%input, "pnm_", $self)
+ or return undef;
if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
$self->{ERRSTR}='unable to write pnm image';
return undef;
}
$self->{DEBUG} && print "writing a pnm file\n";
} elsif ( $input{'type'} eq 'raw' ) {
+ $self->_set_opts(\%input, "raw_", $self)
+ or return undef;
if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
$self->{ERRSTR}='unable to write raw image';
return undef;
}
$self->{DEBUG} && print "writing a raw file\n";
} elsif ( $input{'type'} eq 'png' ) {
+ $self->_set_opts(\%input, "png_", $self)
+ or return undef;
if ( !i_writepng_wiol($self->{IMG}, $IO) ) {
$self->{ERRSTR}='unable to write png image';
return undef;
}
$self->{DEBUG} && print "writing a png file\n";
} elsif ( $input{'type'} eq 'jpeg' ) {
+ $self->_set_opts(\%input, "jpeg_", $self)
+ or return undef;
+ $self->_set_opts(\%input, "exif_", $self)
+ or return undef;
if ( !i_writejpeg_wiol($self->{IMG}, $IO, $input{jpegquality})) {
$self->{ERRSTR} = $self->_error_as_msg();
return undef;
}
$self->{DEBUG} && print "writing a jpeg file\n";
} elsif ( $input{'type'} eq 'bmp' ) {
+ $self->_set_opts(\%input, "bmp_", $self)
+ or return undef;
if ( !i_writebmp_wiol($self->{IMG}, $IO) ) {
$self->{ERRSTR}='unable to write bmp image';
return undef;
}
$self->{DEBUG} && print "writing a bmp file\n";
} elsif ( $input{'type'} eq 'tga' ) {
+ $self->_set_opts(\%input, "tga_", $self)
+ or return undef;
if ( !i_writetga_wiol($self->{IMG}, $IO, $input{wierdpack}, $input{compress}, $input{idstring}) ) {
$self->{ERRSTR}=$self->_error_as_msg();
@@ -1251,6 +1382,8 @@ sub write {
}
$self->{DEBUG} && print "writing a tga file\n";
} elsif ( $input{'type'} eq 'gif' ) {
+ $self->_set_opts(\%input, "gif_", $self)
+ or return undef;
# compatibility with the old interfaces
if ($input{gifquant} eq 'lm') {
$input{make_colors} = 'addi';
@@ -1294,10 +1427,14 @@ sub write_multi {
$class->_set_error('Usage: Imager->write_multi({ options }, @images)');
return 0;
}
+ $class->_set_opts($opts, "i_", @images)
+ or return;
my @work = map $_->{IMG}, @images;
my ($IO, $file) = $class->_get_writer_io($opts, $opts->{'type'})
or return undef;
if ($opts->{'type'} eq 'gif') {
+ $class->_set_opts($opts, "gif_", @images)
+ or return;
my $gif_delays = $opts->{gif_delays};
local $opts->{gif_delays} = $gif_delays;
if ($opts->{gif_delays} && !ref $opts->{gif_delays}) {
@@ -1309,6 +1446,10 @@ sub write_multi {
return $res;
}
elsif ($opts->{'type'} eq 'tiff') {
+ $class->_set_opts($opts, "tiff_", @images)
+ or return;
+ $class->_set_opts($opts, "exif_", @images)
+ or return;
my $res;
$opts->{fax_fine} = 1 unless exists $opts->{fax_fine};
if ($opts->{'class'} && $opts->{'class'} eq 'fax') {
View
22 Imager.xs
@@ -447,6 +447,7 @@ static struct value_name make_color_names[] =
{ "none", mc_none, },
{ "webmap", mc_web_map, },
{ "addi", mc_addi, },
+ { "mediancut", mc_median_cut, },
};
static struct value_name translate_names[] =
@@ -644,6 +645,7 @@ static void cleanup_quant_opts(i_quantize *quant) {
myfree(quant->ed_map);
}
+#if 0
/* look through the hash for options to add to opts */
static void handle_gif_opts(i_gif_opts *opts, HV *hv)
{
@@ -725,6 +727,8 @@ static void cleanup_gif_opts(i_gif_opts *opts) {
myfree(opts->positions);
}
+#endif
+
/* copies the color map from the hv into the colors member of the HV */
static void copy_colors_back(HV *hv, i_quantize *quant) {
SV **sv;
@@ -1936,7 +1940,6 @@ i_writegif_gen(fd, ...)
PROTOTYPE: $$@
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
@@ -1949,9 +1952,7 @@ i_writegif_gen(fd, ...)
hv = (HV *)SvRV(ST(1));
memset(&quant, 0, sizeof(quant));
quant.mc_size = 256;
- memset(&opts, 0, sizeof(opts));
handle_quant_opts(&quant, hv);
- handle_gif_opts(&opts, hv);
img_count = items - 2;
RETVAL = 1;
if (img_count < 1) {
@@ -1975,7 +1976,7 @@ i_writegif_gen(fd, ...)
}
}
if (RETVAL) {
- RETVAL = i_writegif_gen(&quant, fd, imgs, img_count, &opts);
+ RETVAL = i_writegif_gen(&quant, fd, imgs, img_count);
}
myfree(imgs);
if (RETVAL) {
@@ -1985,7 +1986,6 @@ i_writegif_gen(fd, ...)
ST(0) = sv_newmortal();
if (RETVAL == 0) ST(0)=&PL_sv_undef;
else sv_setiv(ST(0), (IV)RETVAL);
- cleanup_gif_opts(&opts);
cleanup_quant_opts(&quant);
@@ -1994,7 +1994,6 @@ i_writegif_callback(cb, maxbuffer,...)
int maxbuffer;
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
@@ -2008,9 +2007,7 @@ i_writegif_callback(cb, maxbuffer,...)
hv = (HV *)SvRV(ST(2));
memset(&quant, 0, sizeof(quant));
quant.mc_size = 256;
- memset(&opts, 0, sizeof(opts));
handle_quant_opts(&quant, hv);
- handle_gif_opts(&opts, hv);
img_count = items - 3;
RETVAL = 1;
if (img_count < 1) {
@@ -2031,7 +2028,7 @@ i_writegif_callback(cb, maxbuffer,...)
}
if (RETVAL) {
wd.sv = ST(0);
- RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count, &opts);
+ RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count);
}
myfree(imgs);
if (RETVAL) {
@@ -2041,7 +2038,6 @@ i_writegif_callback(cb, maxbuffer,...)
ST(0) = sv_newmortal();
if (RETVAL == 0) ST(0)=&PL_sv_undef;
else sv_setiv(ST(0), (IV)RETVAL);
- cleanup_gif_opts(&opts);
cleanup_quant_opts(&quant);
undef_int
@@ -2049,7 +2045,6 @@ i_writegif_wiol(ig, opts,...)
Imager::IO ig
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
@@ -2062,9 +2057,7 @@ i_writegif_wiol(ig, opts,...)
hv = (HV *)SvRV(ST(1));
memset(&quant, 0, sizeof(quant));
quant.mc_size = 256;
- memset(&opts, 0, sizeof(opts));
handle_quant_opts(&quant, hv);
- handle_gif_opts(&opts, hv);
img_count = items - 2;
RETVAL = 1;
if (img_count < 1) {
@@ -2084,7 +2077,7 @@ i_writegif_wiol(ig, opts,...)
}
}
if (RETVAL) {
- RETVAL = i_writegif_wiol(ig, &quant, &opts, imgs, img_count);
+ RETVAL = i_writegif_wiol(ig, &quant, imgs, img_count);
}
myfree(imgs);
if (RETVAL) {
@@ -2094,7 +2087,6 @@ i_writegif_wiol(ig, opts,...)
ST(0) = sv_newmortal();
if (RETVAL == 0) ST(0)=&PL_sv_undef;
else sv_setiv(ST(0), (IV)RETVAL);
- cleanup_gif_opts(&opts);
cleanup_quant_opts(&quant);
void
View
645 gif.c
@@ -595,8 +595,12 @@ i_img **i_readgif_multi_low(GifFileType *GifFile, int *count) {
i_tags_addn(&img->tags, "gif_localmap", 0, 1);
}
if (got_gce) {
- if (trans_index >= 0)
+ if (trans_index >= 0) {
+ i_color trans;
i_tags_addn(&img->tags, "gif_trans_index", 0, trans_index);
+ i_getcolors(img, trans_index, &trans, 1);
+ i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
+ }
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);
@@ -891,10 +895,8 @@ undef_int
i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color fixed[]) {
i_color colors[256];
i_quantize quant;
- i_gif_opts opts;
memset(&quant, 0, sizeof(quant));
- memset(&opts, 0, sizeof(opts));
quant.make_colors = mc_addi;
quant.mc_colors = colors;
quant.mc_size = 1<<max_colors;
@@ -902,7 +904,7 @@ i_writegif(i_img *im, int fd, int max_colors, int pixdev, int fixedlen, i_color
memcpy(colors, fixed, fixedlen * sizeof(i_color));
quant.translate = pt_perturb;
quant.perturb = pixdev;
- return i_writegif_gen(&quant, fd, &im, 1, &opts);
+ return i_writegif_gen(&quant, fd, &im, 1);
}
/*
@@ -920,16 +922,16 @@ undef_int
i_writegifmc(i_img *im, int fd, int max_colors) {
i_color colors[256];
i_quantize quant;
- i_gif_opts opts;
+
+/* *(char *)0 = 1; */
memset(&quant, 0, sizeof(quant));
- memset(&opts, 0, sizeof(opts));
quant.make_colors = mc_none; /* ignored for pt_giflib */
quant.mc_colors = colors;
quant.mc_size = 1 << max_colors;
quant.mc_count = 0;
quant.translate = pt_giflib;
- return i_writegif_gen(&quant, fd, &im, 1, &opts);
+ return i_writegif_gen(&quant, fd, &im, 1);
}
@@ -1090,8 +1092,8 @@ Returns non-zero on success.
=cut
*/
static undef_int
-do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data) {
- if (opts->interlace) {
+do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
+ if (interlace) {
int i, j;
for (i = 0; i < 4; ++i) {
for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
@@ -1131,27 +1133,31 @@ Returns non-zero on success.
=cut
*/
-static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
+static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
{
unsigned char gce[4] = {0};
int want_gce = 0;
+ int delay;
+ int user_input;
+ int disposal_method;
+
if (want_trans) {
gce[0] |= 1;
gce[3] = trans_index;
++want_gce;
}
- if (index < opts->delay_count) {
- gce[1] = opts->delays[index] % 256;
- gce[2] = opts->delays[index] / 256;
+ if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
+ gce[1] = delay % 256;
+ gce[2] = delay / 256;
++want_gce;
}
- if (index < opts->user_input_count) {
- if (opts->user_input_flags[index])
- gce[0] |= 2;
+ if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
+ && user_input) {
+ gce[0] |= 2;
++want_gce;
}
- if (index < opts->disposal_count) {
- gce[0] |= (opts->disposal[index] & 3) << 2;
+ if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
+ gce[0] |= (disposal_method & 3) << 2;
++want_gce;
}
if (want_gce) {
@@ -1164,6 +1170,34 @@ static int do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans,
}
/*
+=item do_comments(gf, img)
+
+Write any comments in the image.
+
+=cut
+*/
+static int do_comments(GifFileType *gf, i_img *img) {
+ int pos = -1;
+
+ while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
+ if (img->tags.tags[pos].data) {
+ if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
+ return 0;
+ }
+ }
+ else {
+ char buf[50];
+ sprintf(buf, "%d", img->tags.tags[pos].idata);
+ if (EGifPutComment(gf, buf) == GIF_ERROR) {
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
=item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
Internal. Add the Netscape2.0 loop extension block, if requested.
@@ -1174,7 +1208,7 @@ application extension blocks.
=cut
*/
-static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
+static int do_ns_loop(GifFileType *gf, i_img *img)
{
/* EGifPutExtension() doesn't appear to handle application
extension blocks in any way
@@ -1188,7 +1222,8 @@ static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
*/
#if 0
/* yes this was another attempt at supporting the loop extension */
- if (opts->loop_count) {
+ int loop_count;
+ if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
unsigned char nsle[12] = "NETSCAPE2.0";
unsigned char subblock[3];
if (EGifPutExtension(gf, 0xFF, 11, nsle) == GIF_ERROR) {
@@ -1197,8 +1232,8 @@ static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
return 0;
}
subblock[0] = 1;
- subblock[1] = opts->loop_count % 256;
- subblock[2] = opts->loop_count / 256;
+ subblock[1] = loop_count % 256;
+ subblock[2] = loop_count / 256;
if (EGifPutExtension(gf, 0, 3, subblock) == GIF_ERROR) {
gif_push_error();
i_push_error(0, "writing loop extention sub-block");
@@ -1215,20 +1250,21 @@ static int do_ns_loop(GifFileType *gf, i_gif_opts *opts)
}
/*
-=item make_gif_map(i_quantize *quant, i_gif_opts *opts, int want_trans)
+=item make_gif_map(i_quantize *quant, int want_trans)
Create a giflib color map object from an Imager color map.
=cut
*/
-static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
- int want_trans) {
+static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
+ int want_trans) {
GifColorType colors[256];
int i;
int size = quant->mc_count;
int map_size;
ColorMapObject *map;
+ i_color trans;
for (i = 0; i < quant->mc_count; ++i) {
colors[i].Red = quant->mc_colors[i].rgb.r;
@@ -1236,9 +1272,11 @@ static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
colors[i].Blue = quant->mc_colors[i].rgb.b;
}
if (want_trans) {
- colors[size].Red = opts->tran_color.rgb.r;
- colors[size].Green = opts->tran_color.rgb.g;
- colors[size].Blue = opts->tran_color.rgb.b;
+ if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
+ trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
+ colors[size].Red = trans.rgb.r;
+ colors[size].Green = trans.rgb.g;
+ colors[size].Blue = trans.rgb.b;
++size;
}
map_size = 1;
@@ -1263,7 +1301,7 @@ static ColorMapObject *make_gif_map(i_quantize *quant, i_gif_opts *opts,
}
/*
-=item gif_set_version(i_quantize *quant, i_gif_opts *opts)
+=item gif_set_version(i_quantize *quant, i_img *imgs, int count)
We need to call EGifSetGifVersion() before opening the file - put that
common code here.
@@ -1285,11 +1323,12 @@ with readers.
=cut
*/
-static void gif_set_version(i_quantize *quant, i_gif_opts *opts) {
+static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
/* the following crashed giflib
the EGifSetGifVersion() is seriously borked in giflib
it's less borked in the ungiflib beta, but we don't have a mechanism
to distinguish them
+ Needs to be updated to support tags.
if (opts->delay_count
|| opts->user_input_count
|| opts->disposal_count
@@ -1325,10 +1364,11 @@ if they do it builds that palette.
A possible improvement might be to eliminate unused colors in the
images palettes.
-=cut */
+=cut
+*/
static int
-has_common_palette(i_img **imgs, int count, i_quantize *quant, int want_trans,
- i_gif_opts *opts) {
+has_common_palette(i_img **imgs, int count, i_quantize *quant,
+ int want_trans) {
int size = quant->mc_count;
int i, j;
int imgn;
@@ -1338,10 +1378,16 @@ has_common_palette(i_img **imgs, int count, i_quantize *quant, int want_trans,
/* we try to build a common palette here, if we can manage that, then
that's the palette we use */
for (imgn = 0; imgn < count; ++imgn) {
+ int eliminate_unused;
if (imgs[imgn]->type != i_palette_type)
return 0;
- if (opts->eliminate_unused) {
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
+ &eliminate_unused)) {
+ eliminate_unused = 1;
+ }
+
+ if (eliminate_unused) {
i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
int x, y;
memset(used, 0, sizeof(used));
@@ -1420,8 +1466,7 @@ Returns non-zero on success.
*/
static undef_int
-i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
- i_gif_opts *opts) {
+i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
unsigned char *result;
int color_bits;
ColorMapObject *map;
@@ -1429,121 +1474,270 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
int imgn, orig_count, orig_size;
int posx, posy;
int trans_index;
+ i_mempool mp;
+ int *localmaps;
+ int anylocal;
+ i_img **glob_imgs; /* images that will use the global color map */
+ int glob_img_count;
+ i_color *orig_colors = quant->mc_colors;
+ i_color *glob_colors = NULL;
+ int glob_color_count;
+ int glob_map_size;
+ int glob_want_trans;
+ int glob_paletted; /* the global map was made from the image palettes */
+ int colors_paletted;
+ int want_trans;
+ int interlace;
+ int gif_background;
+
+ mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
+ quant, gf, imgs, count));
+
+ /* *((char *)0) = 1; */ /* used to break into the debugger */
+
+ if (count <= 0) {
+ i_push_error(0, "No images provided to write");
+ return 0; /* what are you smoking? */
+ }
- mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d, opts %p)\n",
- quant, gf, imgs, count, opts));
+ i_mempool_init(&mp);
- /**((char *)0) = 1;*/
/* sanity is nice */
if (quant->mc_size > 256)
quant->mc_size = 256;
if (quant->mc_count > quant->mc_size)
quant->mc_count = quant->mc_size;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
+ scrw = 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrw))
+ scrw = 0;
+
+ anylocal = 0;
+ localmaps = i_mempool_alloc(&mp, sizeof(int) * count);
+ glob_imgs = i_mempool_alloc(&mp, sizeof(i_img *) * count);
+ glob_img_count = 0;
+ glob_want_trans = 0;
for (imgn = 0; imgn < count; ++imgn) {
- if (imgn < opts->position_count) {
- if (imgs[imgn]->xsize + opts->positions[imgn].x > scrw)
- scrw = imgs[imgn]->xsize + opts->positions[imgn].x;
- if (imgs[imgn]->ysize + opts->positions[imgn].y > scrh)
- scrh = imgs[imgn]->ysize + opts->positions[imgn].y;
- }
+ posx = posy = 0;
+ i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx);
+ i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy);
+ if (imgs[imgn]->xsize + posx > scrw)
+ scrw = imgs[imgn]->xsize + posx;
+ if (imgs[imgn]->ysize + posy > scrh)
+ scrh = imgs[imgn]->ysize + posy;
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_local_map", 0, localmaps+imgn))
+ localmaps[imgn] = 0;
+ if (localmaps[imgn])
+ anylocal = 1;
else {
- if (imgs[imgn]->xsize > scrw)
- scrw = imgs[imgn]->xsize;
- if (imgs[imgn]->ysize > scrh)
- scrh = imgs[imgn]->ysize;
+ if (imgs[imgn]->channels == 4) {
+ glob_want_trans = 1;
+ }
+ glob_imgs[glob_img_count++] = imgs[imgn];
}
}
-
- if (count <= 0) {
- i_push_error(0, "No images provided to write");
- return 0; /* what are you smoking? */
- }
+ glob_want_trans = glob_want_trans && quant->transp != tr_none ;
orig_count = quant->mc_count;
orig_size = quant->mc_size;
- if (opts->each_palette) {
- int want_trans = quant->transp != tr_none
- && imgs[0]->channels == 4;
-
- /* if the caller gives us too many colours we can't do transparency */
- if (want_trans && quant->mc_count == 256)
- want_trans = 0;
- /* if they want transparency but give us a big size, make it smaller
- to give room for a transparency colour */
- if (want_trans && quant->mc_size == 256)
+ if (glob_img_count) {
+ /* this is ugly */
+ glob_colors = i_mempool_alloc(&mp, sizeof(i_color) * quant->mc_size);
+ quant->mc_colors = glob_colors;
+ memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
+ /* we have some images that want to use the global map */
+ if (glob_want_trans && quant->mc_count == 256) {
+ mm_log((2, " disabling transparency for global map - no space\n"));
+ glob_want_trans = 0;
+ }
+ if (glob_want_trans && quant->mc_size == 256) {
+ mm_log((2, " reserving color for transparency\n"));
--quant->mc_size;
-
- /* we always generate a global palette - this lets systems with a
- broken giflib work */
- if (has_common_palette(imgs, 1, quant, want_trans, opts)) {
- result = quant_paletted(quant, imgs[0]);
}
- else {
- quant_makemap(quant, imgs, 1);
- result = quant_translate(quant, imgs[0]);
+ if (has_common_palette(glob_imgs, glob_img_count, quant, want_trans)) {
+ glob_paletted = 1;
}
- if (want_trans) {
- trans_index = quant->mc_count;
- quant_transparent(quant, result, imgs[0], trans_index);
+ else {
+ glob_paletted = 0;
+ quant_makemap(quant, glob_imgs, glob_img_count);
}
+ glob_color_count = quant->mc_count;
+ quant->mc_colors = orig_colors;
+ }
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject."));
- return 0;
+ /* use the global map if we have one, otherwise use the local map */
+ gif_background = 0;
+ if (glob_colors) {
+ quant->mc_colors = glob_colors;
+ quant->mc_count = glob_color_count;
+ want_trans = glob_want_trans && imgs[0]->channels == 4;
+
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
+ gif_background = 0;
+ if (gif_background < 0)
+ gif_background = 0;
+ if (gif_background >= glob_color_count)
+ gif_background = 0;
+ }
+ else {
+ want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
+ if (has_common_palette(imgs, 1, quant, want_trans)) {
+ colors_paletted = 1;
}
-
- color_bits = 1;
- while (quant->mc_size > (1 << color_bits))
+ else {
+ colors_paletted = 0;
+ quant_makemap(quant, imgs, 1);
+ }
+ }
+ if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ mm_log((1, "Error in MakeMapObject"));
+ return 0;
+ }
+ color_bits = 1;
+ if (anylocal) {
+ /* since we don't know how big some the local palettes could be
+ we need to base the bits on the maximum number of colors */
+ while (orig_size > (1 << color_bits))
+ ++color_bits;
+ }
+ else {
+ int count = quant->mc_count;
+ if (want_trans)
+ ++count;
+ while (count > (1 << color_bits))
++color_bits;
+ }
- if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save screen descriptor");
- FreeMapObject(map);
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutScreenDesc."));
- return 0;
- }
+ if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
+ gif_background, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ gif_push_error();
+ i_push_error(0, "Could not save screen descriptor");
FreeMapObject(map);
+ myfree(result);
+ EGifCloseFile(gf);
+ mm_log((1, "Error in EGifPutScreenDesc."));
+ return 0;
+ }
+ FreeMapObject(map);
- if (!do_ns_loop(gf, opts))
- return 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
+ posx = 0;
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
+ posy = 0;
- if (!do_gce(gf, 0, opts, want_trans, trans_index)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (opts->position_count) {
- posx = opts->positions[0].x;
- posy = opts->positions[0].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
+ if (!localmaps[0]) {
+ map = NULL;
+ colors_paletted = glob_paletted;
+ }
+ else {
+ /* if this image has a global map the colors in quant don't
+ belong to this image, so build a palette */
+ if (glob_colors) {
+ /* generate the local map for this image */
+ quant->mc_colors = orig_colors;
+ quant->mc_size = orig_size;
+ quant->mc_count = orig_count;
+ want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
+
+ /* if the caller gives us too many colours we can't do transparency */
+ if (want_trans && quant->mc_count == 256)
+ want_trans = 0;
+ /* if they want transparency but give us a big size, make it smaller
+ to give room for a transparency colour */
+ if (want_trans && quant->mc_size == 256)
+ --quant->mc_size;
+ if (has_common_palette(imgs, 1, quant, want_trans)) {
+ colors_paletted = 1;
+ }
+ else {
+ colors_paletted = 0;
+ quant_makemap(quant, imgs, 1);
+ }
+ if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ EGifCloseFile(gf);
+ mm_log((1, "Error in MakeMapObject"));
+ return 0;
+ }
}
- if (!do_write(gf, opts, imgs[0], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
+ else {
+ /* the map we wrote was the map for this image - don't set the local
+ map */
+ map = NULL;
}
+ }
+
+ if (colors_paletted)
+ result = quant_paletted(quant, imgs[0]);
+ else
+ result = quant_translate(quant, imgs[0]);
+ if (want_trans) {
+ quant_transparent(quant, result, imgs[0], quant->mc_count);
+ trans_index = quant->mc_count;
+ }
+
+ if (!do_ns_loop(gf, imgs[0])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ return 0;
+ }
+
+ if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
myfree(result);
- for (imgn = 1; imgn < count; ++imgn) {
+ EGifCloseFile(gf);
+ return 0;
+ }
+
+ if (!do_comments(gf, imgs[0])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ myfree(result);
+ EGifCloseFile(gf);
+ return 0;
+ }
+
+ if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
+ interlace = 0;
+ if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
+ interlace, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ gif_push_error();
+ i_push_error(0, "Could not save image descriptor");
+ EGifCloseFile(gf);
+ mm_log((1, "Error in EGifPutImageDesc."));
+ return 0;
+ }
+ if (map)
+ FreeMapObject(map);
+
+ if (!do_write(gf, interlace, imgs[0], result)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ EGifCloseFile(gf);
+ myfree(result);
+ return 0;
+ }
+ myfree(result);
+
+ /* that first awful image is out of the way, do the rest */
+ for (imgn = 1; imgn < count; ++imgn) {
+ if (localmaps[imgn]) {
+ quant->mc_colors = orig_colors;
quant->mc_count = orig_count;
quant->mc_size = orig_size;
+
want_trans = quant->transp != tr_none
- && imgs[0]->channels == 4;
+ && imgs[imgn]->channels == 4;
/* if the caller gives us too many colours we can't do transparency */
if (want_trans && quant->mc_count == 256)
want_trans = 0;
@@ -1552,7 +1746,7 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
if (want_trans && quant->mc_size == 256)
--quant->mc_size;
- if (has_common_palette(imgs+imgn, 1, quant, want_trans, opts)) {
+ if (has_common_palette(imgs+imgn, 1, quant, want_trans)) {
result = quant_paletted(quant, imgs[imgn]);
}
else {
@@ -1564,173 +1758,88 @@ i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count,
trans_index = quant->mc_count;
}
- if (!do_gce(gf, imgn, opts, want_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject."));
- return 0;
- }
- if (imgn < opts->position_count) {
- posx = opts->positions[imgn].x;
- posy = opts->positions[imgn].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
- imgs[imgn]->ysize, opts->interlace,
- map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- myfree(result);
- FreeMapObject(map);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
- }
- FreeMapObject(map);
-
- if (!do_write(gf, opts, imgs[imgn], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
+ if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
+ myfree(result);
+ EGifCloseFile(gf);
+ mm_log((1, "Error in MakeMapObject."));
+ return 0;
}
- myfree(result);
- }
- }
- else {
- int want_trans;
- int do_quant_paletted = 0;
-
- /* get a palette entry for the transparency iff we have an image
- with an alpha channel */
- want_trans = 0;
- for (imgn = 0; imgn < count; ++imgn) {
- if (imgs[imgn]->channels == 4) {
- ++want_trans;
- break;
- }
- }
- want_trans = want_trans && quant->transp != tr_none
- && quant->mc_count < 256;
- if (want_trans && quant->mc_size == 256)
- --quant->mc_size;
-
- /* handle the first image separately - since we allow giflib
- conversion and giflib doesn't give us a separate function to build
- the colormap. */
-
- /* produce a colour map */
- if (has_common_palette(imgs, count, quant, want_trans, opts)) {
- result = quant_paletted(quant, imgs[0]);
- ++do_quant_paletted;
}
else {
- quant_makemap(quant, imgs, count);
- result = quant_translate(quant, imgs[0]);
+ quant->mc_colors = glob_colors;
+ quant->mc_count = glob_color_count;
+ if (glob_paletted)
+ result = quant_paletted(quant, imgs[imgn]);
+ else
+ result = quant_translate(quant, imgs[imgn]);
+ want_trans = glob_want_trans && imgs[imgn]->channels == 4;
+ if (want_trans) {
+ quant_transparent(quant, result, imgs[imgn], quant->mc_count);
+ trans_index = quant->mc_count;
+ }
+ map = NULL;
}
- if ((map = make_gif_map(quant, opts, want_trans)) == NULL) {
+ if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
myfree(result);
EGifCloseFile(gf);
- mm_log((1, "Error in MakeMapObject"));
return 0;
}
- color_bits = 1;
- while (quant->mc_count > (1 << color_bits))
- ++color_bits;
- if (EGifPutScreenDesc(gf, scrw, scrh, color_bits, 0, map) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save screen descriptor");
- FreeMapObject(map);
+ if (!do_comments(gf, imgs[imgn])) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
myfree(result);
EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutScreenDesc."));
return 0;
}
- FreeMapObject(map);
-
- if (!do_ns_loop(gf, opts))
- return 0;
- if (!do_gce(gf, 0, opts, want_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (opts->position_count) {
- posx = opts->positions[0].x;
- posy = opts->positions[0].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
+ posx = 0;
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
+ posy = 0;
+
+ if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
+ interlace = 0;
+ if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
+ imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
gif_push_error();
i_push_error(0, "Could not save image descriptor");
+ myfree(result);
+ if (map)
+ FreeMapObject(map);
EGifCloseFile(gf);
mm_log((1, "Error in EGifPutImageDesc."));
return 0;
}
- if (want_trans && imgs[0]->channels == 4)
- quant_transparent(quant, result, imgs[0], quant->mc_count);
-
- if (!do_write(gf, opts, imgs[0], result)) {
+ if (map)
+ FreeMapObject(map);
+
+ if (!do_write(gf, interlace, imgs[imgn], result)) {
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
EGifCloseFile(gf);
myfree(result);
return 0;
}
myfree(result);
-
- for (imgn = 1; imgn < count; ++imgn) {
- int local_trans;
- if (do_quant_paletted)
- result = quant_paletted(quant, imgs[imgn]);
- else
- result = quant_translate(quant, imgs[imgn]);
- local_trans = want_trans && imgs[imgn]->channels == 4;
- if (local_trans)
- quant_transparent(quant, result, imgs[imgn], quant->mc_count);
- if (!do_gce(gf, imgn, opts, local_trans, quant->mc_count)) {
- myfree(result);
- EGifCloseFile(gf);
- return 0;
- }
- if (imgn < opts->position_count) {
- posx = opts->positions[imgn].x;
- posy = opts->positions[imgn].y;
- }
- else
- posx = posy = 0;
- if (EGifPutImageDesc(gf, posx, posy,
- imgs[imgn]->xsize, imgs[imgn]->ysize,
- opts->interlace, NULL) == GIF_ERROR) {
- gif_push_error();
- i_push_error(0, "Could not save image descriptor");
- myfree(result);
- EGifCloseFile(gf);
- mm_log((1, "Error in EGifPutImageDesc."));
- return 0;
- }
- if (!do_write(gf, opts, imgs[imgn], result)) {
- EGifCloseFile(gf);
- myfree(result);
- return 0;
- }
- myfree(result);
- }
}
+
if (EGifCloseFile(gf) == GIF_ERROR) {
+ i_mempool_destroy(&mp);
gif_push_error();
i_push_error(0, "Could not close GIF file");
mm_log((1, "Error in EGifCloseFile\n"));
return 0;
}
+ i_mempool_destroy(&mp);
+ quant->mc_colors = orig_colors;
return 1;
}
@@ -1750,15 +1859,14 @@ Returns non-zero on success.
*/
undef_int
-i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
- i_gif_opts *opts) {
+i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count) {
GifFileType *gf;
i_clear_error();
- mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d, opts %p)\n",
- quant, fd, imgs, count, opts));
+ mm_log((1, "i_writegif_gen(quant %p, fd %d, imgs %p, count %d)\n",
+ quant, fd, imgs, count));
- gif_set_version(quant, opts);
+ gif_set_version(quant, imgs, count);
if ((gf = EGifOpenFileHandle(fd)) == NULL) {
gif_push_error();
@@ -1767,7 +1875,7 @@ i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count,
return 0;
}
- return i_writegif_low(quant, gf, imgs, count, opts);
+ return i_writegif_low(quant, gf, imgs, count);
}
#if IM_GIFMAJOR >= 4
@@ -1802,7 +1910,7 @@ Returns non-zero on success.
undef_int
i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
- int maxlength, i_img **imgs, int count, i_gif_opts *opts)
+ int maxlength, i_img **imgs, int count)
{
#if IM_GIFMAJOR >= 4
GifFileType *gf;
@@ -1811,8 +1919,8 @@ i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
i_clear_error();
- mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d, opts %p)\n",
- quant, cb, userdata, maxlength, imgs, count, opts));
+ mm_log((1, "i_writegif_callback(quant %p, i_write_callback_t %p, userdata $p, maxlength %d, imgs %p, count %d)\n",
+ quant, cb, userdata, maxlength, imgs, count));
if ((gf = EGifOpen(gwd, &gif_writer_callback)) == NULL) {
gif_push_error();
@@ -1822,7 +1930,7 @@ i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata,
return 0;
}
- result = i_writegif_low(quant, gf, imgs, count, opts);
+ result = i_writegif_low(quant, gf, imgs, count);
return free_gen_write_data(gwd, result);
#else
i_clear_error();
@@ -1849,7 +1957,7 @@ io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
=cut
*/
undef_int
-i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts, i_img **imgs,
+i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
int count) {
io_glue_commit_types(ig);
@@ -1861,7 +1969,7 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts, i_img **imgs,
}
/* giflib opens the fd with fdopen(), which is then closed when fclose()
is called - dup it so the caller's fd isn't closed */
- return i_writegif_gen(quant, fd, imgs, count, opts);
+ return i_writegif_gen(quant, fd, imgs, count);
}
else {
#if IM_GIFMAJOR >= 4
@@ -1870,7 +1978,7 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts, i_img **imgs,
i_clear_error();
- gif_set_version(quant, opts);
+ gif_set_version(quant, imgs, count);
if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
gif_push_error();
@@ -1879,7 +1987,7 @@ i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts, i_img **imgs,
return 0;
}
- result = i_writegif_low(quant, GifFile, imgs, count, opts);
+ result = i_writegif_low(quant, GifFile, imgs, count);
ig->closecb(ig);
@@ -1902,7 +2010,8 @@ returns a string that describes that error.
The returned pointer points to a static buffer, either from a literal
C string or a static buffer.
-=cut */
+=cut
+*/
static char const *gif_error_msg(int code) {
static char msg[80];
View
32 image.h
@@ -391,6 +391,7 @@ typedef enum i_make_colors_tag {
mc_none, /* user supplied colour map only */
mc_web_map, /* Use the 216 colour web colour map */
mc_addi, /* Addi's algorithm */
+ mc_median_cut, /* median cut - similar to giflib, hopefully */
mc_mask = 0xFF /* (mask for generator) */
} i_make_colors;
@@ -560,9 +561,9 @@ extern i_img **i_readgif_multi_wiol(io_glue *ig, 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);
-undef_int i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count, i_gif_opts *opts);
-undef_int i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata, int maxbuffer, i_img **imgs, int count, i_gif_opts *opts);
-undef_int i_writegif_wiol(io_glue *ig, i_quantize *quant, i_gif_opts *opts,
+undef_int i_writegif_gen(i_quantize *quant, int fd, i_img **imgs, int count);
+undef_int i_writegif_callback(i_quantize *quant, i_write_callback_t cb, char *userdata, int maxbuffer, i_img **imgs, int count);
+undef_int i_writegif_wiol(io_glue *ig, i_quantize *quant,
i_img **imgs, int count);
void i_qdist(i_img *im);
@@ -724,22 +725,29 @@ 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 int i_tags_addn(i_img_tags *tags, char const *name, int code,
+ int idata);
+extern int i_tags_add(i_img_tags *tags, char const *name, int code,
+ char const *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_find(i_img_tags *tags, char const *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_delbyname(i_img_tags *tags, char const *name);
extern int i_tags_delbycode(i_img_tags *tags, int code);
-extern int i_tags_get_float(i_img_tags *tags, char *name, int code,
+extern int i_tags_get_float(i_img_tags *tags, char const *name, int code,
double *value);
-extern int i_tags_set_float(i_img_tags *tags, char *name, int code,
+extern int i_tags_set_float(i_img_tags *tags, char const *name, int code,
double value);
-extern int i_tags_get_int(i_img_tags *tags, char *name, int code, int *value);
-extern int i_tags_get_string(i_img_tags *tags, char *name, int code,
+extern int i_tags_get_int(i_img_tags *tags, char const *name, int code,
+ int *value);
+extern int i_tags_get_string(i_img_tags *tags, char const *name, int code,
char *value, size_t value_size);
+extern int i_tags_get_color(i_img_tags *tags, char const *name, int code,
+ i_color *value);
+extern int i_tags_set_color(i_img_tags *tags, char const *name, int code,
+ i_color const *value);
extern void i_tags_print(i_img_tags *tags);
#endif
View
3  io.c
@@ -203,6 +203,8 @@ myfree_file_line(void *p, char *file, int line) {
malloc_pointers[i].ptr = NULL;
match++;
}
+
+ mm_log((1, "myfree_file_line: freeing address %p (real %p)\n", pp, pp-UNDRRNVAL));
if (match != 1) {
mm_log((1, "myfree_file_line: INCONSISTENT REFCOUNT %d at %s (%i)\n", match, file, line));
@@ -210,7 +212,6 @@ myfree_file_line(void *p, char *file, int line) {
exit(255);
}
- mm_log((1, "myfree_file_line: freeing address %p (real %p)\n", pp, pp-UNDRRNVAL));
free(pp-UNDRRNVAL);
}
View
172 lib/Imager/Files.pod
@@ -149,6 +149,23 @@ Return either a valid Imager file type, or undef.
The different image formats can write different image type, and some have
different options to control how the images are written.
+When you call C<write()> or C<write_multi()> with an option that has
+the same name as a tag for the image format you're writing, then the
+value supplied to that option will be used to set the corresponding
+tag in the image. Depending on the image format, these values will be
+used when writing the image.
+
+This replaces the previous options that were used when writing GIF
+images. Currently if you use an obsolete option, it will be converted
+to the equivalent tag and Imager will produced a warning. You can
+suppress these warnings by calling the C<Imager::init()> function with
+the C<warn_obsolete> option set to false:
+
+ Imager::init(warn_obsolete=>0);
+
+At some point in the future these obsolete options will no longer be
+supported.
+
=head2 PNM (Portable aNy Map)
Imager can write PGM (Portable Gray Map) and PPM (Portable PixMaps)
@@ -182,75 +199,9 @@ PNM does not support the spatial resolution tags.
=head2 GIF (Graphics Interchange Format)
-You can supply many different options when writing to a GIF file, and
-you can write a multi-image GIF file, eg. for animation, with the
-C<write_multi()> method.
-
-These options can be specified when calling write_multi() or when
-writing a single image with the C<gifquant> option set to 'gen'
-
-Note that some viewers will ignore some of these options
-(C<gif_user_input> in particular).
-
-=over
-
-=item gif_each_palette
-
-Each image in the gif file has it's own palette if this is non-zero.
-All but the first image has a local colour table (the first uses the
-global colour table.
-
-=item interlace
-
-The images are written interlaced if this is non-zero.
-
-=item gif_delays
-
-A reference to an array containing the delays between images, in 1/100
-seconds.
-
-If you want the same delay for every frame you can simply set this to
-the delay in 1/100 seconds.
-
-=item gif_user_input
-
-A reference to an array contains user input flags. If the given flag
-is non-zero the image viewer should wait for input before displaying
-the next image.
-
-=item gif_disposal
-
-A reference to an array of image disposal methods. These define what
-should be done to the image before displaying the next one. These are
-integers, where 0 means unspecified, 1 means the image should be left
-in place, 2 means restore to background colour and 3 means restore to
-the previous value.
-
-=item gif_tran_color
-
-A reference to an Imager::Color object, which is the colour to use for
-the palette entry used to represent transparency in the palette. You
-need to set the transp option (see L<Quantization options>) for this
-value to be used.
-
-=item gif_positions
-
-A reference to an array of references to arrays which represent screen
-positions for each image.
-
-=item gif_loop_count
-
-If this is non-zero the Netscape loop extension block is generated,
-which makes the animation of the images repeat.
-
-This is currently unimplemented due to some limitations in giflib.
-
-=item gif_eliminate_unused
-
-If this is true, when you write a paletted image any unused colors
-will be eliminated from its palette. This is set by default.
-
-=back
+When writing one of more GIF images you can use the same
+L<Quantization Options|Imager::ImageTypes> as you can when converting
+an RGB image into a paletted image.
When reading a GIF all of the sub-images are combined using the screen
size and image positions into one big image, producing an RGB image.
@@ -266,8 +217,8 @@ use the C<getcolors()> method on each image.
GIF does not support the spatial resolution tags.
-GIF will set the following tags in each image when reading, but does
-not use them when saving to GIF:
+Imager will set the following tags in each image when reading, and can
+use most of them when writing to GIF:
=over
@@ -288,24 +239,42 @@ non-zero if the image was interlaced ("Interlace Flag")
=item gif_screen_height
-the size of the logical screen ("Logical Screen Width",
-"Logical Screen Height")
+the size of the logical screen. When writing this is used as the
+minimum. If any image being written would extend beyond this the
+screen size is extended. ("Logical Screen Width", "Logical Screen
+Height").
+
+When writing this is used as a minimum, if the combination of the
+image size and the image's C<gif_left> and C<gif_top> is beyond this
+size then the screen size will be expanded.
=item gif_local_map
-Non-zero if this image had a local color map.
+Non-zero if this image had a local color map. If set for an image
+when writing the image is quantized separately from the other images
+in the file.
=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.
+colormap. You can set this on write too, but for it to choose the
+color you want, you will need to supply only paletted images and set
+the C<gif_eliminate_unused> tag to 0.
=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")
+the alpha set to zero in this palette entry. This value is not used
+when writing. ("Transparent Color Index")
+
+=item gif_trans_color
+
+A reference to an Imager::Color object, which is the colour to use for
+the palette entry used to represent transparency in the palette. You
+need to set the transp option (see L<Quantization options>) for this
+value to be used.
=item gif_delay
@@ -329,11 +298,59 @@ the number of loops from the Netscape Loop extension. This may be zero.
the first block of the first gif comment before each image.
+=item gif_eliminate_unused
+
+If this is true, when you write a paletted image any unused colors
+will be eliminated from its palette. This is set by default.
+
=back
Where applicable, the ("name") is the name of that field from the GIF89
standard.
+The following gif writing options are obsolete, you should set the
+corresponding tag in the image, either by using the tags functions, or
+by supplying the tag and value as options.
+
+=over
+
+=item gif_each_palette
+
+Each image in the gif file has it's own palette if this is non-zero.
+All but the first image has a local colour table (the first uses the
+global colour table.
+
+Use C<gif_local_map> in new code.
+
+=item interlace
+
+The images are written interlaced if this is non-zero.
+
+Use C<gif_interlace> in new code.
+
+=item gif_delays
+
+A reference to an array containing the delays between images, in 1/100
+seconds.
+
+Use C<gif_delay> in new code.
+
+=item gif_positions
+
+A reference to an array of references to arrays which represent screen
+positions for each image.
+
+New code should use the C<gif_left> and C<gif_top> tags.
+
+=item gif_loop_count
+
+If this is non-zero the Netscape loop extension block is generated,
+which makes the animation of the images repeat.
+
+This is currently unimplemented due to some limitations in giflib.
+
+=back
+
=head2 TIFF (Tagged Image File Format)
Imager can write images to either paletted or RGB TIFF images,
@@ -539,5 +556,8 @@ When saving Gif images the program does NOT try to shave of extra
colors if it is possible. If you specify 128 colors and there are
only 2 colors used - it will have a 128 colortable anyway.
+=head1 SEE ALSO
+
+Imager(3)
=cut
View
2  lib/Imager/Matrix2d.pm
@@ -118,7 +118,7 @@ sub translate {
0, 1, $opts{'y'},
0, 0, 1 ], $class;
}
-
+
$Imager::ERRSTR = 'x and y parameters required';
return undef;
}
View
267 quant.c
@@ -5,6 +5,7 @@
#include "image.h"
static void makemap_addi(i_quantize *, i_img **imgs, int count);
+static void makemap_mediancut(i_quantize *, i_img **imgs, int count);
static
void
@@ -27,11 +28,17 @@ setcol(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char
void
quant_makemap(i_quantize *quant, i_img **imgs, int count) {
-#ifdef HAVE_LIBGIF
- /* giflib does it's own color table generation */
- if (quant->translate == pt_giflib)
+
+ if (quant->translate == pt_giflib) {
+ /* giflib does it's own color table generation */
+ /* previously we used giflib's quantizer, but it didn't handle multiple
+ images, which made it hard to build a global color map
+ We've implemented our own median cut code so we can ignore
+ the giflib version */
+ makemap_mediancut(quant, imgs, count);
return;
-#endif
+ }
+
switch (quant->make_colors & mc_mask) {
case mc_none:
/* use user's specified map */
@@ -48,6 +55,10 @@ quant_makemap(i_quantize *quant, i_img **imgs, int count) {
}
break;
+ case mc_median_cut:
+ makemap_mediancut(quant, imgs, count);
+ break;
+
case mc_addi:
default:
makemap_addi(quant, imgs, count);
@@ -73,13 +84,8 @@ i_palidx *quant_translate(i_quantize *quant, i_img *img) {
result = mymalloc(img->xsize * img->ysize);
switch (quant->translate) {
-#ifdef HAVE_LIBGIF
- case pt_giflib:
- translate_giflib(quant, img, result);
- break;
-#endif
-
case pt_closest:
+ case pt_giflib:
translate_closest(quant, img, result);
break;
@@ -497,6 +503,247 @@ makemap_addi(i_quantize *quant, i_img **imgs, int count) {
myfree(clr);
}
+typedef struct {
+ i_sample_t rgb[3];
+ int count;
+} quant_color_entry;
+
+#define MEDIAN_CUT_COLORS 32768
+
+#define MED_CUT_INDEX(c) ((((c).rgb.r & 0xF8) << 7) | \
+ (((c).rgb.g & 0xF8) << 2) | (((c).rgb.b & 0xF8) >> 3))
+
+/* scale these to cover the whole range */
+#define MED_CUT_RED(index) ((((index) & 0x7C00) >> 10) * 255 / 31)
+#define MED_CUT_GREEN(index) ((((index) & 0x3E0) >> 5) * 255 / 31)
+#define MED_CUT_BLUE(index) (((index) & 0x1F) * 255 / 31)
+
+typedef struct {
+ i_sample_t min[3]; /* minimum for each channel */
+ i_sample_t max[3]; /* maximum for each channel */
+ i_sample_t width[3]; /* width for each channel */
+ int start, size; /* beginning and size of the partition */
+ int pixels; /* number of pixels represented by this partition */
+} medcut_partition;
+
+/*
+=item calc_part(part, colors)
+
+Calculates the new color limits for the given partition.
+
+Giflib assumes that the limits for the non-split channels stay the
+same, but this strikes me as incorrect, especially if the colors tend
+to be color ramps.
+
+Of course this could be optimized by not recalculating the channel we
+just sorted on, but it's not worth the effort right now.
+
+=cut
+*/
+static void calc_part(medcut_partition *part, quant_color_entry *colors) {
+ int i, ch;
+
+ for (ch = 0; ch < 3; ++ch) {
+ part->min[ch] = 255;
+ part->max[ch] = 0;
+ }
+ for (i = part->start; i < part->start + part->size; ++i) {
+ for (ch = 0; ch < 3; ++ch) {
+ if (part->min[ch] > colors[i].rgb[ch])
+ part->min[ch] = colors[i].rgb[ch];
+ if (part->max[ch] < colors[i].rgb[ch])
+ part->max[ch] = colors[i].rgb[ch];
+ }
+ }
+ for (ch = 0; ch < 3; ++ch) {
+ part->width[ch] = part->max[ch] - part->min[ch];
+ }
+}
+
+/* simple functions to sort by each channel - we could use a global, but
+ that would be bad */
+
+static int
+color_sort_red(void const *left, void const *right) {
+ return ((quant_color_entry *)left)->rgb[0] - ((quant_color_entry *)right)->rgb[0];
+}
+
+static int
+color_sort_green(void const *left, void const *right) {
+ return ((quant_color_entry *)left)->rgb[1] - ((quant_color_entry *)right)->rgb[1];
+}
+
+static int
+color_sort_blue(void const *left, void const *right) {
+ return ((quant_color_entry *)left)->rgb[2] - ((quant_color_entry *)right)->rgb[2];
+}
+
+static int (*sorters[])(void const *, void const *) =
+{
+ color_sort_red,
+ color_sort_green,
+ color_sort_blue,
+};
+
+static void
+makemap_mediancut(i_quantize *quant, i_img **imgs, int count) {
+ quant_color_entry *colors;
+ i_mempool mp;
+ int imgn, x, y, i, ch;
+ int max_width;
+ i_color *line;
+ int color_count;
+ int total_pixels;
+ medcut_partition *parts;
+ int part_num;
+ int in, out;
+
+ /*printf("images %d pal size %d\n", count, quant->mc_size);*/
+
+ i_mempool_init(&mp);
+
+ colors = i_mempool_alloc(&mp, sizeof(*colors) * MEDIAN_CUT_COLORS);
+ for (i = 0; i < MEDIAN_CUT_COLORS; ++i) {
+ colors[i].rgb[0] = MED_CUT_RED(i);
+ colors[i].rgb[1] = MED_CUT_GREEN(i);
+ colors[i].rgb[2] = MED_CUT_BLUE(i);
+ colors[i].count = 0;
+ }
+
+ max_width = -1;
+ for (imgn = 0; imgn < count; ++imgn) {
+ if (imgs[imgn]->xsize > max_width)
+ max_width = imgs[imgn]->xsize;
+ }
+ line = i_mempool_alloc(&mp, sizeof(i_color) * max_width);
+
+ /* build the stats */
+ total_pixels = 0;
+ for (imgn = 0; imgn < count; ++imgn) {
+ total_pixels += imgs[imgn]->xsize * imgs[imgn]->ysize;
+ for (y = 0; y < imgs[imgn]->ysize; ++y) {
+ i_glin(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
+ for (x = 0; x < imgs[imgn]->xsize; ++x) {
+ ++colors[MED_CUT_INDEX(line[x])].count;
+ }
+ }
+ }
+
+ /* eliminate the empty colors */
+ out = 0;
+ for (in = 0; in < MEDIAN_CUT_COLORS; ++in) {
+ if (colors[in].count) {
+ colors[out++] = colors[in];
+ }
+ }
+ /*printf("out %d\n", out);
+
+ for (i = 0; i < out; ++i) {
+ if (colors[i].count) {
+ printf("%d: (%d,%d,%d) -> %d\n", i, colors[i].rgb[0], colors[i].rgb[1],
+ colors[i].rgb[2], colors[i].count);
+ }
+ }*/
+
+ if (out < quant->mc_size) {
+ /* just copy them into the color table */
+ for (i = 0; i < out; ++i) {
+ for (ch = 0; ch < 3; ++ch) {
+ quant->mc_colors[i].channel[ch] = colors[i].rgb[ch];
+ }
+ }
+ quant->mc_count = out;
+ }
+ else {
+ /* build the starting partition */
+ parts = i_mempool_alloc(&mp, sizeof(*parts) * quant->mc_size);
+ parts[0].start = 0;
+ parts[0].size = out;
+ parts[0].pixels = total_pixels;
+ calc_part(parts, colors);
+ color_count = 1;
+
+ while (color_count < quant->mc_size) {
+ int max_index, max_ch; /* index/channel with biggest spread */
+ int max_size;
+ medcut_partition *workpart;
+ int cum_total;
+ int half;
+
+ /* find the partition with the most biggest span with more than
+ one color */
+ max_size = -1;
+ for (i = 0; i < color_count; ++i) {
+ for (ch = 0; ch < 3; ++ch) {
+ if (parts[i].width[ch] > max_size
+ && parts[i].size > 1) {
+ max_index = i;
+ max_ch = ch;
+ max_size = parts[i].width[ch];
+ }
+ }
+ }
+
+ /* nothing else we can split */
+ if (max_size == -1)
+ break;
+
+ workpart = parts+max_index;
+ /*printf("splitting partition %d (pixels %ld, start %d, size %d)\n", max_index, workpart->pixels, workpart->start, workpart->size);*/
+ qsort(colors + workpart->start, workpart->size, sizeof(*colors),
+ sorters[max_ch]);
+
+ /* find the median or something like it we need to make sure both
+ sides of the split have at least one color in them, so we don't
+ test at the first or last entry */
+ i = workpart->start;
+ cum_total = colors[i].count;
+ ++i;
+ half = workpart->pixels / 2;
+ while (i < workpart->start + workpart->size - 1
+ && cum_total < half) {
+ cum_total += colors[i++].count;
+ }
+ /*printf("Split at %d to make %d (half %ld, cumtotal %ld)\n", i, color_count, half, cum_total);*/
+
+ /* found the spot to split */
+ parts[color_count].start = i;
+ parts[color_count].size = workpart->start + workpart->size - i;
+ workpart->size = i - workpart->start;
+ parts[color_count].pixels = workpart->pixels - cum_total;
+ workpart->pixels = cum_total;
+
+ /* recalculate the limits */
+ calc_part(workpart, colors);
+ calc_part(parts+color_count, colors);
+ ++color_count;
+ }
+
+ /* fill in the color table - since we could still have partitions
+ that have more than one color, we need to average the colors */
+ for (part_num = 0; part_num < color_count; ++part_num) {
+ long sums[3];
+ medcut_partition *workpart;
+
+ workpart = parts+part_num;
+ for (ch = 0; ch < 3; ++ch)
+ sums[ch] = 0;
+
+ for (i = workpart->start; i < workpart->start + workpart->size; ++i) {
+ for (ch = 0; ch < 3; ++ch) {
+ sums[ch] += colors[i].rgb[ch] * colors[i].count;
+ }
+ }
+ for (ch = 0; ch < 3; ++ch) {
+ quant->mc_colors[part_num].channel[ch] = sums[ch] / workpart->pixels;
+ }
+ }
+ quant->mc_count = color_count;
+ }
+ /*printf("out %d colors\n", quant->mc_count);*/
+ i_mempool_destroy(&mp);
+}
+
#define pboxjump 32
/* Define one of the following 4 symbols to choose a colour search method
View
50 t/t105gif.t
@@ -1,7 +1,7 @@
#!perl -w
use strict;
$|=1;
-print "1..40\n";
+print "1..45\n";
use Imager qw(:all);
sub ok ($$$);
@@ -25,11 +25,11 @@ i_box_filled($timg, 0, 0, 20, 20, $green);
i_box_filled($timg, 2, 2, 18, 18, $trans);
if (!i_has_format("gif")) {
- for (1..40) { print "ok $_ # skip no gif support\n"; }
+ for (1..45) { print "ok $_ # skip no gif support\n"; }
} else {
open(FH,">testout/t105.gif") || die "Cannot open testout/t105.gif\n";
binmode(FH);
- i_writegifmc($img,fileno(FH),7) || die "Cannot write testout/t105.gif\n";
+ i_writegifmc($img,fileno(FH),6) || die "Cannot write testout/t105.gif\n";
close(FH);
print "ok 1\n";
@@ -145,6 +145,7 @@ if (!i_has_format("gif")) {
my $sortagreen = i_color_new(0, 255, 0, 63);
for my $i (0..4) {
my $im = Imager::ImgRaw::new(200, 200, 4);
+ _add_tags($im, gif_delay=>50, gif_disposal=>2);
for my $j (0..$i-1) {
my $fill = i_color_new(0, 128, 0, 255 * ($i-$j)/$i);
i_box_filled($im, 0, $j*40, 199, $j*40+40, $fill);
@@ -215,11 +216,13 @@ EOS
print "ok 14 # skip giflib3 doesn't support callbacks\n";
}
@imgs = ();
+ my $c = i_color_new(0,0,0,0);
for my $g (0..3) {
my $im = Imager::ImgRaw::new(200, 200, 3);
+ _add_tags($im, gif_local_map=>1, gif_delay=>150, gif_loop=>10);
for my $x (0 .. 39) {
for my $y (0 .. 39) {
- my $c = i_color_new($x * 6, $y * 6, 32*$g+$x+$y, 255);
+ $c->set($x * 6, $y * 6, 32*$g+$x+$y, 255);
i_box_filled($im, $x*5, $y*5, $x*5+4, $y*5+4, $c);
}
}
@@ -232,11 +235,8 @@ EOS
# output looks moderately horrible
open FH, ">testout/t105_mult_pall.gif" or die "Cannot create file: $!";
binmode FH;
- if (i_writegif_gen(fileno(FH), { make_colors=>'webmap',
+ 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";
}
@@ -449,6 +449,25 @@ EOS
"re-reading saved paletted images");
ok(39, i_img_diff($imgs[0], $imgs2[0]) == 0, "imgs[0] mismatch");
ok(40, i_img_diff($imgs[1], $imgs2[1]) == 0, "imgs[1] mismatch");
+
+ # test that the OO interface warns when we supply old options
+ {
+ my @warns;
+ local $SIG{__WARN__} = sub { push(@warns, "@_") };
+
+ my $ooim = Imager->new;
+ ok(41, $ooim->read(file=>"testout/t105.gif"), "read into object");
+ ok(42, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
+ "save from object");
+ ok(43, grep(/Obsolete .* interlace .* gif_interlace/, @warns),
+ "check for warning");
+ init(warn_obsolete=>0);
+ @warns = ();
+ ok(44, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
+ "save from object");
+ ok(45, !grep(/Obsolete .* interlace .* gif_interlace/, @warns),
+ "check for warning");
+ }
}
sub ok ($$$) {
@@ -489,3 +508,18 @@ sub read_failure {
close FH;
}
+sub _clear_tags {
+ my (@imgs) = @_;
+
+ for my $img (@imgs) {
+ $img->deltag(code=>0);
+ }
+}
+
+sub _add_tags {
+ my ($img, %tags) = @_;
+
+ for my $key (keys %tags) {
+ Imager::i_tags_add($img, $key, 0, $tags{$key}, 0);
+ }
+}
View
2  t/t106tiff.t
@@ -147,7 +147,7 @@ if (!i_has_format("tiff")) {
# OO to data
$ooim->write(data=>\$oodata, type=>'tiff')
- or print 'not ';
+ or print "# ",$ooim->errstr, "\nnot ";
print "ok 19\n";
$oodata eq $tiffdata or print "not ";
print "ok 20\n";
View
3  t/t50basicoo.t
@@ -50,7 +50,8 @@ my %files;
{ file => "testout/t108_24bit.tga" }, );
my %writeopts =
(
- gif=> { make_colors=>'webmap', translate=>'closest', gifquant=>'gen' },
+ gif=> { make_colors=>'webmap', translate=>'closest', gifquant=>'gen',
+ gif_delay=>20 },
);
for my $type (@types) {
View
164 tags.c
@@ -47,6 +47,8 @@ A tag is represented by an i_img_tag structure:
#include "image.h"
#include <string.h>
#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
/* useful for debugging */
void i_tags_print(i_img_tags *tags);
@@ -79,7 +81,7 @@ Returns non-zero on success.
=cut
*/
-int i_tags_addn(i_img_tags *tags, char *name, int code, int idata) {
+int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
return i_tags_add(tags, name, code, NULL, 0, idata);
}
@@ -95,9 +97,11 @@ Returns non-zero on success.
=cut
*/
-int i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size,
- int idata) {
+int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data,
+ int size, int idata) {
i_img_tag work = {0};
+ /*printf("i_tags_add(tags %p [count %d], name %s, code %d, data %p, size %d, idata %d)\n",
+ tags, tags->count, name, code, data, size, idata);*/
if (tags->tags == NULL) {
int alloc = 10;
tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
@@ -134,6 +138,8 @@ int i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size,
work.idata = idata;
tags->tags[tags->count++] = work;
+ /*i_tags_print(tags);*/
+
return 1;
}
@@ -150,7 +156,7 @@ void i_tags_destroy(i_img_tags *tags) {
}
}
-int i_tags_find(i_img_tags *tags, char *name, int start, int *entry) {
+int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
if (tags->tags) {
while (start < tags->count) {
if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
@@ -177,23 +183,28 @@ int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
}
int i_tags_delete(i_img_tags *tags, int entry) {
+ /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
+ tags, tags->count, 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);
+ (tags->count-entry-1) * sizeof(i_img_tag));
if (old.name)
myfree(old.name);
if (old.data)
myfree(old.data);
--tags->count;
+
return 1;
}
return 0;
}
-int i_tags_delbyname(i_img_tags *tags, char *name) {
+int i_tags_delbyname(i_img_tags *tags, char const *name) {
int count = 0;
int i;
+ /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
+ tags, tags->count, name);*/
if (tags->tags) {
for (i = tags->count-1; i >= 0; --i) {
if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
@@ -202,6 +213,8 @@ int i_tags_delbyname(i_img_tags *tags, char *name) {
}
}
}
+ /*i_tags_print(tags);*/
+
return count;
}
@@ -219,7 +232,8 @@ int i_tags_delbycode(i_img_tags *tags, int code) {
return count;
}
-int i_tags_get_float(i_img_tags *tags, char *name, int code, double *value) {
+int i_tags_get_float(i_img_tags *tags, char const *name, int code,
+ double *value) {
int index;
i_img_tag *entry;
@@ -240,7 +254,8 @@ int i_tags_get_float(i_img_tags *tags, char *name, int code, double *value) {
return 1;
}
-int i_tags_set_float(i_img_tags *tags, char *name, int code, double value) {
+int i_tags_set_float(i_img_tags *tags, char const *name, int code,
+ double value) {
char temp[40];
sprintf(temp, "%.30g", value);
@@ -252,7 +267,7 @@ int i_tags_set_float(i_img_tags *tags, char *name, int code, double value) {
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 i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
int index;
i_img_tag *entry;
@@ -273,7 +288,132 @@ int i_tags_get_int(i_img_tags *tags, char *name, int code, int *value) {
return 1;
}
-int i_tags_get_string(i_img_tags *tags, char *name, int code,
+static int parse_long(char *data, char **end, long *out) {
+#if 0
+ /* I wrote this without thinking about strtol */
+ long x = 0;
+ int neg = *data == '-';
+
+ if (neg)
+ ++data;
+ if (!isdigit(*data))
+ return 0;
+ while (isdigit(*data)) {
+ /* this check doesn't guarantee we don't overflow, but it helps */
+ if (x > LONG_MAX / 10)
+ return 0;
+ x = x * 10 + *data - '0';
+ ++data;
+ }
+ if (neg)
+ x = -x;
+
+ *end = data;
+
+ return 1;
+#else
+ long result;
+ int savederr = errno;
+ char *myend;
+
+ errno = 0;
+ result = strtol(data, &myend, 10);
+ if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE
+ || myend == data) {
+ return 0;
+ }
+
+ *out = result;
+ *end = myend;
+
+ return 1;
+#endif
+}
+
+/* parse a comma-separated list of integers
+ returns when it has maxcount numbers, finds a non-comma after a number
+ or can't parse a number
+ if it can't parse a number after a comma, that's considered an error
+*/
+static int parse_long_list(char *data, char **end, int maxcount, long *out) {
+ int i;
+
+ while (i < maxcount-1) {
+ if (!parse_long(data, &data, out))
+ return 0;
+ out++;
+ i++;
+ if (*data != ',')
+ return i;
+ ++data;
+ }
+ if (!parse_long(data, &data, out))
+ return 0;
+ ++i;
+ *end = data;
+ return i;
+}
+
+/* parse "color(red,green,blue,alpha)" */
+static int parse_color(char *data, char **end, i_color *value) {
+ long n[4];
+ int count, i;
+
+ if (memcmp(data, "color(", 6))
+ return 0; /* not a color */
+ data += 6;
+ count = parse_long_list(data, &data, 4, n);
+ if (count < 3)
+ return 0;
+ for (i = 0; i < count; ++i)
+ value->channel[i] = n[i];
+ if (count < 4)
+ value->channel[3] = 255;
+
+ return 1;
+}
+
+int i_tags_get_color(i_img_tags *tags, char const *name, int code,
+ i_color *value) {
+ int index;
+ i_img_tag *entry;
+ char *end;
+
+ 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)
+ return 0;
+
+ if (!parse_color(entry->data, &end, value))
+ return 0;
+
+ /* for now we're sloppy about the end */
+
+ return 1;
+}