- 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
=================================================================
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;
};
$FORMATGUESS=\&def_guess_type;
+
+ $warn_obsolete = 1;
}
#
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' )) {
}
}
+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;
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;
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
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';
}
}
} 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();
}
$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';
$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}) {
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') {
{ "none", mc_none, },
{ "webmap", mc_web_map, },
{ "addi", mc_addi, },
+ { "mediancut", mc_median_cut, },
};
static struct value_name translate_names[] =
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)
{
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;
PROTOTYPE: $$@
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
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) {
}
}
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) {
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);
int maxbuffer;
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
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) {
}
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) {
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
Imager::IO ig
PREINIT:
i_quantize quant;
- i_gif_opts opts;
i_img **imgs = NULL;
int img_count;
int i;
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) {
}
}
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) {
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
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);
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;
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);
}
/*
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);
}
=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]) {
=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) {
return 1;
}
+/*
+=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)
=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
*/
#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) {
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");
}
/*
-=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;
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;
}
/*
-=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.
=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
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;
/* 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));
*/
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;
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;
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 {
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;
}
*/
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();
return 0;
}
- return i_writegif_low(quant, gf, imgs, count, opts);
+ return i_writegif_low(quant, gf, imgs, count);
}
#if IM_GIFMAJOR >= 4
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;
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();
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();
=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);
}
/* 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
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();
return 0;
}
- result = i_writegif_low(quant, GifFile, imgs, count, opts);
+ result = i_writegif_low(quant, GifFile, imgs, count);
ig->closecb(ig);
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];
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;
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);
/* 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
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));
exit(255);
}
- mm_log((1, "myfree_file_line: freeing address %p (real %p)\n", pp, pp-UNDRRNVAL));
free(pp-UNDRRNVAL);
}
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)
=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.
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
=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
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,
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
0, 1, $opts{'y'},
0, 0, 1 ], $class;
}
-
+
$Imager::ERRSTR = 'x and y parameters required';
return undef;
}
#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
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 */
}
break;
+ case mc_median_cut:
+ makemap_mediancut(quant, imgs, count);
+ break;
+
case mc_addi:
default:
makemap_addi(quant, imgs, count);
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;
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
#!perl -w
use strict;
$|=1;
-print "1..40\n";
+print "1..45\n";
use Imager qw(:all);
sub ok ($$$);
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";
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);
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);
}
}
# 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";
}
"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 ($$$) {
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);
+ }
+}
# 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";
{ 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) {
#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);
=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);
}
=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);
work.idata = idata;
tags->tags[tags->count++] = work;
+ /*i_tags_print(tags);*/
+
return 1;
}
}
}
-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) {
}
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) {
}
}
}
+ /*i_tags_print(tags);*/
+
return count;
}
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;
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);
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;
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;
+}
+
+int i_tags_set_color(i_img_tags *tags, char const *name, int code,
+ i_color const *value) {
+ char temp[80];
+
+ sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
+ value->channel[2], value->channel[3]);
+ 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_string(i_img_tags *tags, char const *name, int code,
char *value, size_t value_size) {
int index;
i_img_tag *entry;
i_img_tag *tag = tags->tags + i;
printf("Tag %d\n", i);
if (tag->name)
- printf(" Name : %s\n", tag->name);
+ printf(" Name : %s (%p)\n", tag->name, tag->name);
printf(" Code : %d\n", tag->code);
if (tag->data) {
int pos;
- printf(" Data : %d => '", tag->size);
+ printf(" Data : %d (%p) => '", tag->size, tag->data);
for (pos = 0; pos < tag->size; ++pos) {
if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
putchar('\\');