Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

- file write plugins

- ICO write support, including extensive tests

- code coverage tests for reading icons/cursors

- write_multi() will now write to scalars, aka the data => \$scalar
output parameter

- added ignores for gcov data
  • Loading branch information...
commit 43c4c3960c72f28a5a1916f7489cdcce8f2621d3 1 parent 6205786
Tony Cook authored
View
10 ICO/ICO.pm
@@ -147,12 +147,20 @@ Imager::File::ICO - read MS Icon files
my @imgs = Imager->read_multi(file => "foo.ico")
or die Imager->errstr;
-=head1 DESCRIPTION
+ $img->write(file => "foo.ico")
+ or die $img->errstr;
+
+ Imager->write_multi({ file => "foo.ico" }, @imgs)
+ or die Imager->errstr;
+=head1 DESCRIPTION
+Imager's MS Icon support is documented in L<Imager::Files>.
=head1 AUTHOR
+Tony Cook <tony@imager.perl.org>
+
=head1 SEE ALSO
Imager, Imager::Files.
View
39 ICO/imicon.c
@@ -220,14 +220,14 @@ validate_image(i_img *im) {
}
static int
-translate_mask(i_img *im, unsigned char *out, unsigned char *in) {
+translate_mask(i_img *im, unsigned char *out, const char *in) {
int x, y;
int one, zero;
int len = strlen(in);
int pos;
int newline; /* set to the first newline type we see */
int notnewline; /* set to whatever in ( "\n\r" newline isn't ) */
-
+
if (len < 3)
return 0;
@@ -245,7 +245,6 @@ translate_mask(i_img *im, unsigned char *out, unsigned char *in) {
y = 0;
while (y < im->ysize && pos < len) {
x = 0;
-
while (x < im->xsize && pos < len) {
if (in[pos] == newline) {
/* don't process it, we look for it later */
@@ -256,10 +255,12 @@ translate_mask(i_img *im, unsigned char *out, unsigned char *in) {
}
else if (in[pos] == one) {
*out++ = 1;
+ ++x;
++pos;
}
else if (in[pos] == zero) {
*out++ = 0;
+ ++x;
++pos;
}
else if (in[pos] == ' ' || in[pos] == '\t') {
@@ -275,6 +276,8 @@ translate_mask(i_img *im, unsigned char *out, unsigned char *in) {
}
while (pos < len && in[pos] != newline)
++pos;
+ if (pos < len && in[pos] == newline)
+ ++pos; /* actually skip the newline */
++y;
}
@@ -287,19 +290,18 @@ translate_mask(i_img *im, unsigned char *out, unsigned char *in) {
}
static void
-derive_mask(i_img *im, unsigned char *out) {
+derive_mask(i_img *im, ico_image_t *ico) {
if (im->channels == 1 || im->channels == 3) {
- int i;
-
- for (i = 0; i < im->xsize * im->ysize; ++i) {
- *out++ = 0;
- }
+ /* msicon.c's default mask is what we want */
+ myfree(ico->mask_data);
+ ico->mask_data = NULL;
}
else {
int channel = im->channels - 1;
i_sample_t *linebuf = mymalloc(sizeof(i_sample_t) * im->xsize);
int x, y;
+ unsigned char *out = ico->mask_data;
for (y = 0; y < im->ysize; ++y) {
i_gsamp(im, 0, im->xsize, y, linebuf, &channel, 1);
@@ -408,14 +410,15 @@ fill_image_base(i_img *im, ico_image_t *ico, const char *mask_name) {
{
/* build the mask */
- /* can't overflow, max icon size too small */
- int mask_size = im->xsize * im->ysize + 4;
- char *mask_buf = mymalloc(mask_size); /* checked */
+ int mask_index;
+
ico->mask_data = mymalloc(im->xsize * im->ysize);
-
- if (!i_tags_get_string(&im->tags, mask_name, 0, mask_buf, mask_size)
- || !translate_mask(im, ico->mask_data, mask_buf)) {
- derive_mask(im, ico->mask_data);
+
+ if (!i_tags_find(&im->tags, mask_name, 0, &mask_index)
+ || !im->tags.tags[mask_index].data
+ || !translate_mask(im, ico->mask_data,
+ im->tags.tags[mask_index].data)) {
+ derive_mask(im, ico);
}
}
}
@@ -471,7 +474,7 @@ i_writeico_multi_wiol(i_io_glue_t *ig, i_img **ims, int count) {
i_clear_error();
- if (count > 255) {
+ if (count > 0xFFFF) {
i_push_error(0, "too many images for ico files");
return 0;
}
@@ -579,7 +582,7 @@ i_writecur_multi_wiol(i_io_glue_t *ig, i_img **ims, int count) {
for (i = 0; i < count; ++i)
fill_image_cursor(ims[i], icons + i);
- if (!ico_write(ig, icons, count, ICON_ICON, &error)) {
+ if (!ico_write(ig, icons, count, ICON_CURSOR, &error)) {
ico_push_error(error);
for (i = 0; i < count; ++i)
unfill_image(icons + i);
View
142 ICO/msicon.c
@@ -155,7 +155,7 @@ ico_reader_open(i_io_glue_t *ig, int *error) {
ico_reader_image_entry *image = file->images + i;
if (type == ICON_ICON) {
- if (!read_packed(ig, "bbxxxxxxdd", &width, &height, &bytes_in_res,
+ if (!read_packed(ig, "bb xxxxxx dd", &width, &height, &bytes_in_res,
&image_offset)) {
free(file->images);
free(file);
@@ -167,7 +167,7 @@ ico_reader_open(i_io_glue_t *ig, int *error) {
else {
long hotspot_x, hotspot_y;
- if (!read_packed(ig, "bbxxwwdd", &width, &height,
+ if (!read_packed(ig, "bb xx ww dd", &width, &height,
&hotspot_x, &hotspot_y, &bytes_in_res,
&image_offset)) {
free(file->images);
@@ -205,7 +205,7 @@ ico_image_count(ico_reader_t *file) {
/*
=item ico_type
- // type of file - 1 for icon, 2 for cursor
+ // type of file - ICON_ICON for icon, ICON_CURSOR for cursor
type = ico_type(file);
=cut
@@ -383,7 +383,7 @@ ico_image_release(ico_image_t *image) {
/*
=item ico_reader_close
-Releases the file structure.
+Releases the read file structure.
=cut
*/
@@ -402,7 +402,36 @@ ico_reader_close(ico_reader_t *file) {
=over
-=item ico_write
+=item ico_write(ig, images, image_count, type, &error)
+
+Parameters:
+
+=over
+
+=item *
+
+io_glue *ig - an Imager IO object. This only needs to implement
+writing for ico_write()
+
+=item *
+
+ico_image_t *images - array of images to be written.
+
+=item *
+
+int image_count - number of images
+
+=item *
+
+int type - must be ICON_ICON or ICON_CURSOR
+
+=item *
+
+int *error - set to an error code on failure.
+
+=back
+
+Returns non-zero on success.
=cut
*/
@@ -899,6 +928,7 @@ read_mask(ico_reader_t *file, ico_image_t *image, int *error) {
unsigned char *read_buffer = malloc(line_bytes);
int y;
int x;
+ int mask;
unsigned char *inp, *outp;
if (!read_buffer) {
@@ -915,10 +945,14 @@ read_mask(ico_reader_t *file, ico_image_t *image, int *error) {
outp = image->mask_data + y * image->width;
inp = read_buffer;
+ mask = 0x80;
for (x = 0; x < image->width; ++x) {
- *outp++ = (*inp >> (7 - (x & 7))) & 1;
- if ((x & 7) == 7)
+ *outp++ = (*inp & mask) ? 1 : 0;
+ mask >>= 1;
+ if (!mask) {
+ mask = 0x80;
++inp;
+ }
}
}
free(read_buffer);
@@ -965,6 +999,14 @@ ico_write_validate(ico_image_t const *images, int image_count, int *error) {
return 1;
}
+/*
+=item ico_image_size
+
+Calculate how much space the icon takes up in the file.
+
+=cut
+*/
+
static int
ico_image_size(ico_image_t const *image, int *bits, int *colors) {
int size = 40; /* start with the BITMAPINFOHEADER */
@@ -988,7 +1030,12 @@ ico_image_size(ico_image_t const *image, int *bits, int *colors) {
*bits = 8;
*colors = 0;
}
- size += (((image->width * 8 + *bits-1) / *bits) + 3) / 4 * 4 * image->height;
+
+ /* palette size */
+ size += *colors * 4;
+
+ /* image data size */
+ size += (image->width * *bits + 31) / 32 * 4 * image->height;
}
/* add in the mask */
@@ -997,6 +1044,14 @@ ico_image_size(ico_image_t const *image, int *bits, int *colors) {
return size;
}
+/*
+=item write_packed
+
+Pack numbers given a format to a stream.
+
+=cut
+*/
+
static int
write_packed(i_io_glue_t *ig, char const *format, ...) {
unsigned char buffer[100];
@@ -1065,6 +1120,14 @@ write_packed(i_io_glue_t *ig, char const *format, ...) {
return 1;
}
+/*
+=item write_palette
+
+Write the palette for an icon.
+
+=cut
+*/
+
static int
write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error) {
int full_size = image->palette_size;
@@ -1111,16 +1174,26 @@ write_palette(i_io_glue_t *ig, ico_image_t const *image, int *error) {
return 1;
}
+/*
+=item write_bitmapinfoheader
+
+Write the BITMAPINFOHEADER for an icon image.
+
+=cut
+*/
+
static int
write_bitmapinfoheader(i_io_glue_t *ig, ico_image_t const *image, int *error,
int bit_count, int clr_used) {
if (!write_packed(ig, "d dd w w d d dd dd",
- 40, /* biSize */
- image->width, 2 * image->height, /* biWidth/biHeight */
+ 40UL, /* biSize */
+ (unsigned long)image->width,
+ (unsigned long)2 * image->height, /* biWidth/biHeight */
1, bit_count, /* biPlanes, biBitCount */
- 0, 0, /* biCompression, biSizeImage */
- 0, 0, /* bi(X|Y)PetsPerMeter */
- clr_used, 0)) { /* biClrUsed, biClrImportant */
+ 0UL, 0UL, /* biCompression, biSizeImage */
+ 0UL, 0UL, /* bi(X|Y)PetsPerMeter */
+ (unsigned long)clr_used, /* biClrUsed */
+ 0UL)) { /* biClrImportant */
*error = ICOERR_Write_Failure;
return 0;
}
@@ -1128,6 +1201,14 @@ write_bitmapinfoheader(i_io_glue_t *ig, ico_image_t const *image, int *error,
return 1;
}
+/*
+=item write_32_bit
+
+Write 32-bit image data to the icon.
+
+=cut
+*/
+
static int
write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
unsigned char *writebuf;
@@ -1167,6 +1248,14 @@ write_32_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
return 1;
}
+/*
+=item write_8_bit
+
+Write 8 bit image data.
+
+=cut
+*/
+
static int
write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
static const unsigned char zeros[3] = { '\0' };
@@ -1198,6 +1287,14 @@ write_8_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
return 1;
}
+/*
+=item write_4_bit
+
+Write 4 bit image data.
+
+=cut
+*/
+
static int
write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
int line_size = ((image->width + 1) / 2 + 3) / 4 * 4;
@@ -1245,6 +1342,14 @@ write_4_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
return 1;
}
+/*
+=item write_1_bit
+
+Write 1 bit image data.
+
+=cut
+*/
+
static int
write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
int line_size = (image->width + 31) / 32 * 4;
@@ -1293,6 +1398,14 @@ write_1_bit(i_io_glue_t *ig, ico_image_t const *image, int *error) {
return 1;
}
+/*
+=item write_mask
+
+Write the AND mask.
+
+=cut
+*/
+
static int
write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error) {
int line_size = (image->width + 31) / 32 * 4;
@@ -1322,6 +1435,7 @@ write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error) {
mask = 0x80;
outp++;
}
+ ++pixelp;
}
if (i_io_write(ig, writebuf, line_size) != line_size) {
*error = ICOERR_Write_Failure;
@@ -1331,7 +1445,7 @@ write_mask(i_io_glue_t *ig, ico_image_t const *image, int *error) {
}
}
else {
- memset(writebuf, 0xff, line_size);
+ memset(writebuf, 0, line_size);
for (y = image->height-1; y >= 0; --y) {
if (i_io_write(ig, writebuf, line_size) != line_size) {
*error = ICOERR_Write_Failure;
View
150 ICO/t/t10icon.t
@@ -1,6 +1,6 @@
#!perl -w
use strict;
-use Test::More tests => 60;
+use Test::More tests => 94;
BEGIN { use_ok('Imager::File::ICO'); }
@@ -173,3 +173,151 @@ ok($im2->read(file=>'testout/t10_32.ico', type=>'ico'),
is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0,
"check they're the same");
is($im->bits, $im2->bits, "check same bits");
+
+{
+ my $im = Imager->new(xsize => 32, ysize => 32);
+ $im->box(filled=>1, color=>'#FF00FF');
+ my $data;
+ ok(Imager->write_multi({ data => \$data, type=>'ico' }, $im, $im),
+ "write multi icons");
+ ok(length $data, "and it wrote data");
+ my @im = Imager->read_multi(data => $data);
+ is(@im, 2, "got all the images back");
+ is(Imager::i_img_diff($im->{IMG}, $im[0]{IMG}), 0, "check first image");
+ is(Imager::i_img_diff($im->{IMG}, $im[1]{IMG}), 0, "check second image");
+}
+
+{ # 1 channel image
+ my $im = Imager->new(xsize => 32, ysize => 32, channels => 1);
+ $im->box(filled=>1, color => [ 128, 0, 0 ]);
+ my $data;
+ ok($im->write(data => \$data, type=>'ico'), "write 1 channel image");
+ my $im2 = Imager->new;
+ ok($im2->read(data => $data), "read it back");
+ is($im2->getchannels, 4, "check channels");
+ my $imrgb = $im->convert(preset => 'rgb')
+ ->convert(preset => 'addalpha');
+ is(Imager::i_img_diff($imrgb->{IMG}, $im2->{IMG}), 0,
+ "check image matches expected");
+}
+
+{ # 2 channel image
+ my $base = Imager->new(xsize => 32, ysize => 32, channels => 2);
+ $base->box(filled => 1, color => [ 64, 192, 0 ]);
+ my $data;
+ ok($base->write(data => \$data, type=>'ico'), "write 2 channel image");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ is($read->getchannels, 4, "check channels");
+ my $imrgb = $base->convert(preset => 'rgb');
+ is(Imager::i_img_diff($imrgb->{IMG}, $read->{IMG}), 0,
+ "check image matches expected");
+}
+
+{ # 4 channel image
+ my $base = Imager->new(xsize => 32, ysize => 32, channels => 4);
+ $base->box(filled=>1, ymax => 15, color => [ 255, 0, 255, 128 ]);
+ $base->box(filled=>1, ymin => 16, color => [ 0, 255, 255, 255 ]);
+ my $data;
+ ok($base->write(data => \$data, type=>'ico'), "write 4 channel image");
+ my $read = Imager->new;
+ ok($read->read(data => $data, type=>'ico'), "read it back")
+ or print "# ", $read->errstr, "\n";
+ is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
+ "check image matches expected");
+}
+
+{ # mask handling
+ my $base = Imager->new(xsize => 16, ysize => 16, channels => 3);
+ $base->box(filled=>1, xmin => 5, xmax => 10, color => '#0000FF');
+ $base->box(filled=>1, ymin => 5, ymax => 10, color => '#0000FF');
+ my $mask = <<EOS; # CR in this to test it's skipped correctly
+01
+0000011111100000
+00000111111 00000xx
+00000111111000
+00000111111000
+0000011111100000
+1111111111111111
+1111111111111111
+1111111111111111
+1111111111111111
+1111111111111111
+1111111111111111
+1010101010101010
+1010101010101010
+1010101010101010
+1010101010101010
+1010101010101010
+EOS
+ $mask =~ s/\n/\r\n/g; # to test alternate newline handling is correct
+ $base->settag(name => 'ico_mask', value => $mask);
+ my $saved_mask = $base->tags(name => 'ico_mask');
+ my $data;
+ ok($base->write(data => \$data, type => 'ico'),
+ "write with mask tag set");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ my $mask2 = $mask;
+ $mask2 =~ tr/01/.*/;
+ $mask2 =~ s/\n$//;
+ $mask2 =~ tr/\r x//d;
+ $mask2 =~ s/^(.{3,19})$/$1 . "." x (16 - length $1)/gem;
+ my $read_mask = $read->tags(name => 'ico_mask');
+ is($read_mask, $mask2, "check mask is correct");
+}
+
+{ # mask too short to handle
+ my $mask = "xx";
+ my $base = Imager->new(xsize => 16, ysize => 16, channels => 3);
+ $base->box(filled=>1, xmin => 5, xmax => 10, color => '#0000FF');
+ $base->box(filled=>1, ymin => 5, ymax => 10, color => '#0000FF');
+ $base->settag(name => 'ico_mask', value => $mask);
+ my $data;
+ ok($base->write(data => \$data, type=>'ico'),
+ "save icon with short mask tag");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ my $read_mask = $read->tags(name => 'ico_mask');
+ my $expected_mask = ".*" . ( "\n" . "." x 16 ) x 16;
+ is($read_mask, $expected_mask, "check the mask");
+
+ # mask that doesn't match what we expect
+ $base->settag(name => 'ico_mask', value => 'abcd');
+ ok($base->write(data => \$data, type => 'ico'),
+ "write with bad format mask tag");
+ ok($read->read(data => $data), "read it back");
+ $read_mask = $read->tags(name => 'ico_mask');
+ is($read_mask, $expected_mask, "check the mask");
+
+ # mask with invalid char
+ $base->settag(name => 'ico_mask', value => ".*\n....xxx..");
+ ok($base->write(data => \$data, type => 'ico'),
+ "write with unexpected chars in mask");
+ ok($read->read(data => $data), "read it back");
+ $read_mask = $read->tags(name => 'ico_mask');
+ is($read_mask, $expected_mask, "check the mask");
+}
+
+{ # check handling of greyscale paletted
+ my $base = Imager->new(xsize => 16, ysize => 16, channels => 1,
+ type => 'paletted');
+ my @grays = map Imager::Color->new($_),
+ "000000", "666666", "CCCCCC", "FFFFFF";
+ ok($base->addcolors(colors => \@grays), "add some colors");
+ $base->box(filled => 1, color => $grays[1], xmax => 7, ymax => 7);
+ $base->box(filled => 1, color => $grays[1], xmax => 7, ymin => 8);
+ $base->box(filled => 1, color => $grays[1], xmin => 8, ymax => 7);
+ $base->box(filled => 1, color => $grays[1], xmin => 8, ymax => 8);
+ my $data;
+ ok($base->write(data => \$data, type => 'ico'),
+ "write grayscale paletted");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back")
+ or print "# ", $read->errstr, "\n";
+ is($read->type, 'paletted', "check type");
+ is($read->getchannels, 3, "check channels");
+ my $as_rgb = $base->convert(preset => 'rgb');
+ is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
+ "check the image");
+}
View
53 ICO/t/t30cursor.t
@@ -1,6 +1,6 @@
#!perl -w
use strict;
-use Test::More tests => 14;
+use Test::More tests => 25;
BEGIN { use_ok('Imager::File::CUR'); }
@@ -26,9 +26,48 @@ $im->settag(name => 'cur_hotspoty', value => -1);
ok($im->write(file=>'testout/hotspot.cur', type=>'cur'),
"save with oor hotspot")
or print "# ",$im->errstr, "\n";
-my $im2 = Imager->new;
-ok($im2->read(file=>'testout/hotspot.cur', type=>'cur'),
- "re-read the hotspot set cursor")
- or print "# ", $im->errstr, "\n";
-is($im2->tags(name => 'cur_hotspotx'), 31, "check cur_hotspotx tag");
-is($im2->tags(name => 'cur_hotspoty'), 0, "check cur_hotspoty tag");
+{
+ my $im2 = Imager->new;
+ ok($im2->read(file=>'testout/hotspot.cur', type=>'cur'),
+ "re-read the hotspot set cursor")
+ or print "# ", $im->errstr, "\n";
+ is($im2->tags(name => 'cur_hotspotx'), 31, "check cur_hotspotx tag");
+ is($im2->tags(name => 'cur_hotspoty'), 0, "check cur_hotspoty tag");
+}
+
+$im->settag(name => 'cur_hotspotx', value => -1);
+$im->settag(name => 'cur_hotspoty', value => 32);
+ok($im->write(file=>'testout/hotspot2.cur', type=>'cur'),
+ "save with oor hotspot")
+ or print "# ",$im->errstr, "\n";
+
+{
+ my $im2 = Imager->new;
+ ok($im2->read(file=>'testout/hotspot2.cur', type=>'cur'),
+ "re-read the hotspot set cursor")
+ or print "# ", $im->errstr, "\n";
+ is($im2->tags(name => 'cur_hotspotx'), 0, "check cur_hotspotx tag");
+ is($im2->tags(name => 'cur_hotspoty'), 31, "check cur_hotspoty tag");
+}
+
+{
+ my $data = '';
+ ok($im->write(data => \$data, type => 'cur'),
+ "write single to data");
+ print "# ", length $data, " bytes written\n";
+ my $im2 = Imager->new;
+ ok($im2->read(data => $data), "read back in");
+ is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0, "check image");
+}
+
+{
+ my $data = '';
+ ok(Imager->write_multi({ type => 'cur', data => \$data }, $im, $im),
+ "write multiple images");
+ print "# ", length $data, " bytes written\n";
+ my @im = Imager->read_multi(type => 'cur', data => $data)
+ or print "# ", Imager->errstr, "\n";
+ is(@im, 2, "read them back in");
+ is(Imager::i_img_diff($im->{IMG}, $im[0]{IMG}), 0, "check first image");
+ is(Imager::i_img_diff($im->{IMG}, $im[1]{IMG}), 0, "check second image");
+}
View
144 ICO/t/t60writefail.t
@@ -1,7 +1,7 @@
#!perl -w
use strict;
-use Test::More tests => 24;
-use Imager;
+use Test::More tests => 69;
+use Imager ':handy';
# this file tries to cover as much of the write error handling cases in
# msicon.c/imicon.c as possible.
@@ -105,9 +105,149 @@ use Imager;
Imager->_set_error('');
}
+{
+ my $im = Imager->new(xsize => 10, ysize => 10);
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(6), maxbuffer => 1),
+ "second write (resource) should fail (ico)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ $im->_set_error('');
+
+ ok(!$im->write(type => 'cur', callback => WriteLimit->new(6), maxbuffer => 1),
+ "second (resource) write should fail (cur)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ $im->_set_error('');
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(22), maxbuffer => 1),
+ "third write (bmi) should fail (32-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ $im->_set_error('');
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(62), maxbuffer => 1),
+ "fourth write (data) should fail (32-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ $im->_set_error('');
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(462), maxbuffer => 1),
+ "mask write should fail (32-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+}
+
+{ # 1 bit write fails
+ my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+ my $red = NC(255, 0, 0);
+ my $blue = NC(0, 0, 255);
+ $im->addcolors(colors => [ $red, $blue ]);
+ $im->box(filled => 1, color => $red, ymax => 5);
+ $im->box(filled => 1, color => $blue, ymin => 6);
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(22), maxbuffer => 1),
+ "third write (bmi) should fail (1-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(66), maxbuffer => 1),
+ "fourth write (palette) should fail (1-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(74), maxbuffer => 1),
+ "fifth write (image) should fail (1-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ my $data;
+ ok($im->write(data => \$data, type => 'ico'), "write 1 bit successfully");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ is($read->type, 'paletted', "check type");
+ is($read->tags(name => 'ico_bits'), 1, "check bits");
+ is(Imager::i_img_diff($read, $im), 0, "check image correct");
+}
+
+{ # 4 bit write fails
+ my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+ my $red = NC(255, 0, 0);
+ my $blue = NC(0, 0, 255);
+ $im->addcolors(colors => [ ($red, $blue) x 8 ]);
+ $im->box(filled => 1, color => $red, ymax => 5);
+ $im->box(filled => 1, color => $blue, ymin => 6);
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(22), maxbuffer => 1),
+ "third write (bmi) should fail (4-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(66), maxbuffer => 1),
+ "fourth write (palette) should fail (4-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(130), maxbuffer => 1),
+ "fifth write (image) should fail (4-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ my $data;
+ ok($im->write(data => \$data, type => 'ico'), "write 4 bit successfully");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ is($read->type, 'paletted', "check type");
+ is($read->tags(name => 'ico_bits'), 4, "check bits");
+ is(Imager::i_img_diff($read, $im), 0, "check image correct");
+}
+
+{ # 8 bit write fails
+ my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+ my $red = NC(255, 0, 0);
+ my $blue = NC(0, 0, 255);
+ $im->addcolors(colors => [ ($red, $blue) x 9 ]);
+ $im->box(filled => 1, color => $red, ymax => 5);
+ $im->box(filled => 1, color => $blue, ymin => 6);
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(22), maxbuffer => 1),
+ "third write (bmi) should fail (8-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(62), maxbuffer => 1),
+ "fourth write (palette) should fail (8-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(62 + 1024), maxbuffer => 1),
+ "fifth write (image) should fail (8-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ ok(!$im->write(type => 'ico', callback => WriteLimit->new(62 + 1024 + 10), maxbuffer => 1),
+ "sixth write (zeroes) should fail (8-bit)");
+ is($im->errstr, "Write failure: limit reached", "check message");
+ my $data;
+ ok($im->write(data => \$data, type => 'ico'), "write 8 bit successfully");
+ my $read = Imager->new;
+ ok($read->read(data => $data), "read it back");
+ is($read->type, 'paletted', "check type");
+ is($read->tags(name => 'ico_bits'), 8, "check bits");
+ is(Imager::i_img_diff($read, $im), 0, "check image correct");
+}
+
# write callback that fails
sub write_failure {
print "# synthesized write failure\n";
Imager::i_push_error(0, "synthetic error");
return;
}
+
+package WriteLimit;
+use overload
+ '&{}' => \&limited_write,
+ 'bool' => sub { 1 };
+
+sub new {
+ my ($class, $limit) = @_;
+
+ bless
+ {
+ do_write =>
+ sub {
+ my ($data) = @_;
+ $limit -= length $data;
+ if ($limit >= 0) {
+ print "# write of ", length $data, " bytes successful ($limit left)\n";
+ return 1;
+ }
+ else {
+ print "# write of ", length $data, " bytes failed\n";
+ Imager::i_push_error(0, "limit reached");
+ return;
+ }
+ }
+ },$class;
+}
+
+sub limited_write {
+ my ($self) = @_;
+ return $self->{do_write};
+}
View
11 ICO/t/t70icosing.t
@@ -0,0 +1,11 @@
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+require '../t/testtools.pl';
+
+# checks that we load the ICO write handler automatically
+my $img = test_oo_img();
+ok($img->write(file => 'testout/icosing.ico'),
+ "write ico with autoload")
+ or print "# ",$img->errstr,"\n";
View
11 ICO/t/t71icomult.t
@@ -0,0 +1,11 @@
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+require '../t/testtools.pl';
+
+# checks that we load the ICO write handler automatically
+my $img = test_oo_img();
+ok(Imager->write_multi({ file => 'testout/icomult.ico' }, $img, $img),
+ "write_multi ico with autoload")
+ or print "# ",Imager->errstr,"\n";
View
11 ICO/t/t72cursing.t
@@ -0,0 +1,11 @@
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+require '../t/testtools.pl';
+
+# checks that we load the CUR write handler automatically
+my $img = test_oo_img();
+ok($img->write(file => 'testout/cursing.cur'),
+ "write cur with autoload")
+ or print "# ",$img->errstr,"\n";
View
11 ICO/t/t73curmult.t
@@ -0,0 +1,11 @@
+#!perl -w
+use strict;
+use Test::More tests => 1;
+use Imager;
+require '../t/testtools.pl';
+
+# checks that we load the CUR write handler automatically
+my $img = test_oo_img();
+ok(Imager->write_multi({ file => 'testout/icomult.cur' }, $img, $img),
+ "write_multi cur with autoload")
+ or print "# ",Imager->errstr,"\n";
View
296 Imager.pm
@@ -1405,6 +1405,16 @@ sub _reader_autoload {
++$attempted_to_load{$file};
require $file;
};
+ if ($@) {
+ # try to get a reader specific module
+ my $file = "Imager/File/\U$type\EReader.pm";
+ unless ($attempted_to_load{$file}) {
+ eval {
+ ++$attempted_to_load{$file};
+ require $file;
+ };
+ }
+ }
}
}
@@ -1552,106 +1562,109 @@ sub write {
_writer_autoload($input{type});
+ my ($IO, $fh);
if ($writers{$input{type}} && $writers{$input{type}}{single}) {
- my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
+ ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
or return undef;
- return $writers{$input{type}}{single}->($self, $IO, %input);
- }
-
- if (!$formats{$input{'type'}}) {
- $self->{ERRSTR}='format not supported';
- return undef;
- }
-
- my ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
- or return undef;
-
- 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} = $self->_error_as_msg();
- return undef;
- }
- } else {
- if (!i_writetiff_wiol($self->{IMG}, $IO)) {
- $self->{ERRSTR} = $self->_error_as_msg();
- return undef;
- }
- }
- } elsif ( $input{'type'} eq 'pnm' ) {
- $self->_set_opts(\%input, "pnm_", $self)
- or return undef;
- if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
- $self->{ERRSTR} = $self->_error_as_msg();
- return undef;
- }
- $self->{DEBUG} && print "writing a pnm file\n";
- } elsif ( $input{'type'} eq 'raw' ) {
- $self->_set_opts(\%input, "raw_", $self)
+ $writers{$input{type}}{single}->($self, $IO, %input)
or return undef;
- if ( !i_writeraw_wiol($self->{IMG},$IO) ) {
- $self->{ERRSTR} = $self->_error_as_msg();
- 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();
+ }
+ else {
+ if (!$formats{$input{'type'}}) {
+ $self->{ERRSTR}='format not supported';
return undef;
}
- $self->{DEBUG} && print "writing a tga file\n";
- } elsif ( $input{'type'} eq 'gif' ) {
- $self->_set_opts(\%input, "gif_", $self)
+
+ ($IO, $fh) = $self->_get_writer_io(\%input, $input{'type'})
or return undef;
- # compatibility with the old interfaces
- if ($input{gifquant} eq 'lm') {
- $input{make_colors} = 'addi';
- $input{translate} = 'perturb';
- $input{perturb} = $input{lmdither};
- } elsif ($input{gifquant} eq 'gen') {
- # just pass options through
- } else {
- $input{make_colors} = 'webmap'; # ignored
- $input{translate} = 'giflib';
- }
- if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
- $self->{ERRSTR} = $self->_error_as_msg;
- return;
+
+ 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} = $self->_error_as_msg();
+ return undef;
+ }
+ } else {
+ if (!i_writetiff_wiol($self->{IMG}, $IO)) {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ return undef;
+ }
+ }
+ } elsif ( $input{'type'} eq 'pnm' ) {
+ $self->_set_opts(\%input, "pnm_", $self)
+ or return undef;
+ if ( ! i_writeppm_wiol($self->{IMG},$IO) ) {
+ $self->{ERRSTR} = $self->_error_as_msg();
+ 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} = $self->_error_as_msg();
+ 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();
+ return undef;
+ }
+ $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';
+ $input{translate} = 'perturb';
+ $input{perturb} = $input{lmdither};
+ } elsif ($input{gifquant} eq 'gen') {
+ # just pass options through
+ } else {
+ $input{make_colors} = 'webmap'; # ignored
+ $input{translate} = 'giflib';
+ }
+ if (!i_writegif_wiol($IO, \%input, $self->{IMG})) {
+ $self->{ERRSTR} = $self->_error_as_msg;
+ return;
+ }
}
}
@@ -1687,54 +1700,72 @@ sub write_multi {
or return;
my @work = map $_->{IMG}, @images;
+ _writer_autoload($type);
+
+ my ($IO, $file);
if ($writers{$type} && $writers{$type}{multiple}) {
- my ($IO, $file) = $class->_get_writer_io($opts, $type)
+ ($IO, $file) = $class->_get_writer_io($opts, $type)
or return undef;
- return $writers{$type}{multiple}->($class, $IO, $opts, @images);
- }
-
- if (!$formats{$type}) {
- $class->_set_error("format not $type supported");
- return undef;
- }
-
- my ($IO, $file) = $class->_get_writer_io($opts, $type)
- or return undef;
-
- if ($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}) {
- # assume the caller wants the same delay for each frame
- $opts->{gif_delays} = [ ($gif_delays) x @images ];
- }
- my $res = i_writegif_wiol($IO, $opts, @work);
- $res or $class->_set_error($class->_error_as_msg());
- return $res;
+ $writers{$type}{multiple}->($class, $IO, $opts, @images)
+ or return undef;
}
- elsif ($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') {
- $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
+ else {
+ if (!$formats{$type}) {
+ $class->_set_error("format $type not supported");
+ return undef;
+ }
+
+ ($IO, $file) = $class->_get_writer_io($opts, $type)
+ or return undef;
+
+ if ($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}) {
+ # assume the caller wants the same delay for each frame
+ $opts->{gif_delays} = [ ($gif_delays) x @images ];
+ }
+ unless (i_writegif_wiol($IO, $opts, @work)) {
+ $class->_set_error($class->_error_as_msg());
+ return undef;
+ }
+ }
+ elsif ($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') {
+ $res = i_writetiff_multi_wiol_faxable($IO, $opts->{fax_fine}, @work);
+ }
+ else {
+ $res = i_writetiff_multi_wiol($IO, @work);
+ }
+ unless ($res) {
+ $class->_set_error($class->_error_as_msg());
+ return undef;
+ }
}
else {
- $res = i_writetiff_multi_wiol($IO, @work);
+ $ERRSTR = "Sorry, write_multi doesn't support $type yet";
+ return 0;
}
- $res or $class->_set_error($class->_error_as_msg());
- return $res;
}
- else {
- $ERRSTR = "Sorry, write_multi doesn't support $type yet";
- return 0;
+
+ if (exists $opts->{'data'}) {
+ my $data = io_slurp($IO);
+ if (!$data) {
+ Imager->_set_error('Could not slurp from buffer');
+ return undef;
+ }
+ ${$opts->{data}} = $data;
}
+ return 1;
}
# read multiple images from a file
@@ -3199,6 +3230,7 @@ sub def_guess_type {
return 'rgb' if ($ext eq "rgb");
return 'gif' if ($ext eq "gif");
return 'raw' if ($ext eq "raw");
+ return lc $ext; # best guess
return ();
}
View
9 MANIFEST
@@ -24,9 +24,18 @@ ICO/msicon.h
ICO/t/t10icon.t
ICO/t/t20readone.t
ICO/t/t21readmult.t
+ICO/t/t30cursor.t
+ICO/t/t40readcurone.t
+ICO/t/t41curmultread.t
ICO/t/t50readfail.t
+ICO/t/t60writefail.t
+ICO/t/t70icosing.t
+ICO/t/t71icomult.t
+ICO/t/t72cursing.t
+ICO/t/t73curmult.t
ICO/testimg/combo.ico
ICO/testimg/pal13232.ico
+ICO/testimg/pal43232.cur
ICO/testimg/pal43232.ico
ICO/testimg/pal43232.ppm
ICO/testimg/pal83232.ico
View
4 MANIFEST.SKIP
@@ -44,3 +44,7 @@ Makefile\.old
^ICO/ICO\.c$
^ICO/testout
+# trash from profiling
+\.gcno$
+\.gcda$
+\.gcov$
View
2  Makefile.PL
@@ -871,7 +871,7 @@ YAML
# this is intended to only be running on the development
# machines
sub distcheck {
- if (-e '.svn') {
+ if (-e '.svn' && -e 'Changes') {
# update Changes if needed
my $write_changes;
# get the last revision from Changes
View
190 lib/Imager/Files.pod
@@ -778,6 +778,120 @@ values for the first line and so on, you can use the interleave option:
There are no PNG specific tags.
+=head2 ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)
+
+Icon and Cursor files are very similar, the only differences being a
+number in the header and the storage of the cursor hotspot. I've
+treated them separately so that you're not messing with tags to
+distinguish between them.
+
+The following tags are set when reading an icon image and are used
+when writing it:
+
+=over
+
+=item ico_mask
+
+This is the AND mask of the icon. When used as an icon in Windows 1
+bits in the mask correspond to pixels that are modified by the source
+image rather than simply replaced by the source image.
+
+Rather than requiring a binary bitmap this is accepted in a specific format:
+
+=over
+
+=item *
+
+first line consisting of the 0 placeholder, the 1 placeholder and a
+newline.
+
+=item *
+
+following lines which contain 0 and 1 placeholders for each scanline
+of the image, starting from the top of the image.
+
+=back
+
+When reading an image, '.' is used as the 0 placeholder and '*' as the
+1 placeholder. An example:
+
+ .*
+ ..........................******
+ ..........................******
+ ..........................******
+ ..........................******
+ ...........................*****
+ ............................****
+ ............................****
+ .............................***
+ .............................***
+ .............................***
+ .............................***
+ ..............................**
+ ..............................**
+ ...............................*
+ ...............................*
+ ................................
+ ................................
+ ................................
+ ................................
+ ................................
+ ................................
+ *...............................
+ **..............................
+ **..............................
+ ***.............................
+ ***.............................
+ ****............................
+ ****............................
+ *****...........................
+ *****...........................
+ *****...........................
+ *****...........................
+
+=back
+
+The following tags are set when reading an icon:
+
+=over
+
+=item ico_bits
+
+The number of bits per pixel used to store the image.
+
+=back
+
+For cursor files the following tags are set and read when reading and
+writing:
+
+=over
+
+=item cur_mask
+
+This is the same as the ico_mask above.
+
+=item cur_hotspotx
+
+=item cur_hotspoty
+
+The "hot" spot of the cursor image. This is the spot on the cursor
+that you click with. If you set these to out of range values they are
+clipped to the size of the image when written to the file.
+
+=back
+
+C<cur_bits> is set when reading a cursor.
+
+Examples:
+
+ my $img = Imager->new(xsize => 32, ysize => 32, channels => 4);
+ $im->box(color => 'FF0000');
+ $im->write(file => 'box.ico');
+
+ $im->settag(name => 'cur_hotspotx', value => 16);
+ $im->settag(name => 'cur_hotspoty', value => 16);
+ $im->write(file => 'box.cur');
+
=head1 ADDING NEW FORMATS
To support a new format for reading, call the register_reader() class
@@ -875,18 +989,88 @@ Example:
},
);
+=item register_writer
+
+Registers single or multiple image write functions.
+
+Parameters:
+
+=over
+
+=item *
+
+type - the identifier of the file format. This is typically the
+extension in lowercase.
+
+This parameter is required.
+
+=item *
+
+single - a code ref to write a single image to a file. This is
+supplied:
+
+=over
+
+=item *
+
+the object that write() was called on,
+
+=item *
+
+an Imager::IO object that should be used to write the file, and
+
+=item *
+
+all the parameters supplied to the write() method.
+
+=back
+
+The single parameter is required.
+
+=item *
+
+multiple - a code ref which is called to write multiple images to a
+file. This is supplied:
+
+=over
+
+=item *
+
+the class name write_multi() was called on, this is typically
+C<Imager>.
+
+=item *
+
+an Imager::IO object that should be used to write the file, and
+
+=item *
+
+all the parameters supplied to the read_multi() method.
+
+=back
+
+=back
+
=back
If you name the reader module C<Imager::File::>I<your-format-name>
where I<your-format-name> is a fully upper case version of the type
-value you would pass to read() or read_multi() then Imager will
-attempt to load that module if it has no other way to read that
-format.
+value you would pass to read(), read_multi(), write() or write_multi()
+then Imager will attempt to load that module if it has no other way to
+read or write that format.
For example, if you create a module Imager::File::GIF and the user has
built Imager without it's normal GIF support then an attempt to read a
GIF image will attempt to load Imager::File::GIF.
+If your module can only handle reading then you can name your module
+C<Imager::File::>I<your-format-name>C<Reader> and Imager will attempt
+to autoload it.
+
+If your module can only handle writing then you can name your module
+C<Imager::File::>I<your-format-name>C<Writer> and Imager will attempt
+to autoload it.
+
=head1 EXAMPLES
=head2 Producing an image from a CGI script
View
19 t/t106tiff.t
@@ -1,7 +1,7 @@
#!perl -w
use strict;
use lib 't';
-use Test::More tests => 101;
+use Test::More tests => 106;
use Imager qw(:all);
$^W=1; # warnings during command-line tests
$|=1; # give us some progress in the test harness
@@ -32,7 +32,7 @@ SKIP:
$im = Imager->new(xsize=>2, ysize=>2);
ok(!$im->write(file=>"testout/notiff.tif"), "should fail to write tiff");
is($im->errstr, 'format not supported', "check no tiff message");
- skip("no tiff support", 97);
+ skip("no tiff support", 102);
}
Imager::i_tags_add($img, "i_xres", 0, "300", 0);
@@ -409,4 +409,19 @@ SKIP:
"Error opening file: Not a TIFF (?:or MDI )?file, bad magic number (8483 \\(0x2123\\)|8993 \\(0x2321\\))",
"check error message");
}
+
+ { # write_multi to data
+ my $data;
+ my $im = Imager->new(xsize => 50, ysize => 50);
+ ok(Imager->write_multi({ data => \$data, type=>'tiff' }, $im, $im),
+ "write multi to in memory");
+ ok(length $data, "make sure something written");
+ my @im = Imager->read_multi(data => $data);
+ is(@im, 2, "make sure we can read it back");
+ is(Imager::i_img_diff($im[0]{IMG}, $im->{IMG}), 0,
+ "check first image");
+ is(Imager::i_img_diff($im[1]{IMG}, $im->{IMG}), 0,
+ "check second image");
+ }
}
+
Please sign in to comment.
Something went wrong with that request. Please try again.