static
i_img *
-read_one_icon(ico_reader_t *file, int index) {
+read_one_icon(ico_reader_t *file, int index, int masked) {
ico_image_t *image;
int error;
i_img *result;
return NULL;
}
+ if (masked) {
+ /* check to make sure we should do the masking, if the mask has
+ nothing set we don't mask */
+ int pos;
+ int total = image->width * image->height;
+ unsigned char *inp = image->mask_data;
+
+ masked = 0;
+ for (pos = 0; pos < total; ++pos) {
+ if (*inp++) {
+ masked = 1;
+ break;
+ }
+ }
+ }
+
if (image->direct) {
int x, y;
i_color *line_buf;
int pal_index;
int y;
unsigned char *image_data;
+ int channels = masked ? 4 : 3;
- if (!i_int_check_image_file_limits(image->width, image->height, 3, 1)) {
+ if (!i_int_check_image_file_limits(image->width, image->height, channels, 1)) {
ico_image_release(image);
return NULL;
}
- result = i_img_pal_new(image->width, image->height, 3, 256);
+ result = i_img_pal_new(image->width, image->height, channels, 256);
if (!result) {
ico_image_release(image);
return NULL;
c.rgba.r = image->palette[pal_index].r;
c.rgba.g = image->palette[pal_index].g;
c.rgba.b = image->palette[pal_index].b;
- c.rgba.a = 255; /* so as to not confuse some code */
+ c.rgba.a = 255;
if (i_addcolors(result, &c, 1) < 0) {
i_push_error(0, "could not add color to palette");
myfree(mask);
}
+
+ /* if the user requests, treat the mask as an alpha channel.
+ Note: this converts the image into a direct image if it was paletted
+ */
+ if (masked) {
+ unsigned char *inp = image->mask_data;
+ int x, y;
+ i_color *line_buf = mymalloc(sizeof(i_color) * image->width);
+
+ for (y = 0; y < image->height; ++y) {
+ int changed = 0;
+ int first;
+ int last;
+ for (x = 0; x < image->width; ++x) {
+ if (*inp++) {
+ if (!changed) {
+ first = x;
+ i_glin(result, first, image->width, y, line_buf);
+ changed = 1;
+ }
+ last = x;
+ line_buf[x-first].rgba.a = 0;
+ }
+ }
+ if (changed) {
+ i_plin(result, first, last + 1, y, line_buf);
+ }
+ }
+ myfree(line_buf);
+ }
if (ico_type(file) == ICON_ICON) {
i_tags_setn(&result->tags, "ico_bits", image->bit_count);
i_tags_set(&result->tags, "i_format", "ico", 3);
}
i_img *
-i_readico_single(io_glue *ig, int index) {
+i_readico_single(io_glue *ig, int index, int masked) {
ico_reader_t *file;
i_img *result;
int error;
/* the index is range checked by msicon.c - don't duplicate it here */
- result = read_one_icon(file, index);
+ result = read_one_icon(file, index, masked);
ico_reader_close(file);
return result;
}
i_img **
-i_readico_multi(io_glue *ig, int *count) {
+i_readico_multi(io_glue *ig, int *count, int masked) {
ico_reader_t *file;
int index;
int error;
*count = 0;
for (index = 0; index < ico_image_count(file); ++index) {
- i_img *im = read_one_icon(file, index);
+ i_img *im = read_one_icon(file, index, masked);
if (!im)
break;
#!perl -w
use strict;
-use Test::More tests => 94;
+use Test::More tests => 98;
+use Imager::Test qw(is_image);
BEGIN { use_ok('Imager::File::ICO'); }
my $im = Imager->new;
# type=>'ico' or 'cur' and read ico and cur since they're pretty much
# the same
-ok($im->read(file => "testimg/rgba3232.ico", type=>"ico"),
+ok($im->read(file => "testimg/rgba3232.ico", type=>"ico", ico_masked => 0),
"read 32 bit")
or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
"compare image data");
}
-ok($im->read(file => 'testimg/pal83232.ico', type=>'ico'),
+ok($im->read(file => 'testimg/pal83232.ico', type=>'ico', ico_masked => 0),
"read 8 bit")
or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
}
$im->write(file=>'testout/pal83232.ppm');
-ok($im->read(file => 'testimg/pal43232.ico', type=>'ico'),
+ok($im->read(file => 'testimg/pal43232.ico', type=>'ico', ico_masked => 0),
"read 4 bit")
or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
}
$im->write(file=>'testout/pal43232.ppm');
-ok($im->read(file => 'testimg/pal13232.ico', type=>'ico'),
+ok($im->read(file => 'testimg/pal13232.ico', type=>'ico', ico_masked => 0),
"read 1 bit")
or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
# combo was created with the GIMP, which has a decent mechanism for selecting
# the output format
# you get different size icon images by making different size layers.
-my @imgs = Imager->read_multi(file => 'testimg/combo.ico', type=>'ico');
+my @imgs = Imager->read_multi(file => 'testimg/combo.ico', type=>'ico',
+ ico_masked => 0);
is(scalar(@imgs), 3, "read multiple");
is($imgs[0]->getwidth, 48, "image 0 width");
is($imgs[0]->getheight, 48, "image 0 height");
"write 32-bit icon");
my $im2 = Imager->new;
-ok($im2->read(file=>'testout/t10_32.ico', type=>'ico'),
+ok($im2->read(file=>'testout/t10_32.ico', type=>'ico', ico_masked => 0),
"read it back in");
is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0,
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);
+ my @im = Imager->read_multi(data => $data, ico_masked => 0);
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");
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");
+ ok($im2->read(data => $data, ico_masked => 0), "read it back");
is($im2->getchannels, 4, "check channels");
my $imrgb = $im->convert(preset => 'rgb')
->convert(preset => 'addalpha');
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");
+ ok($read->read(data => $data, ico_masked => 0), "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,
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")
+ ok($read->read(data => $data, type=>'ico', ico_masked => 0), "read it back")
or print "# ", $read->errstr, "\n";
is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
"check image matches expected");
ok($base->write(data => \$data, type => 'ico'),
"write with mask tag set");
my $read = Imager->new;
- ok($read->read(data => $data), "read it back");
+ ok($read->read(data => $data, ico_masked => 0), "read it back");
my $mask2 = $mask;
$mask2 =~ tr/01/.*/;
$mask2 =~ s/\n$//;
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");
+ ok($read->read(data => $data, ico_masked => 0), "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");
$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");
+ ok($read->read(data => $data, ico_masked => 0), "read it back");
$read_mask = $read->tags(name => 'ico_mask');
is($read_mask, $expected_mask, "check the mask");
$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");
+ ok($read->read(data => $data, ico_masked => 0), "read it back");
$read_mask = $read->tags(name => 'ico_mask');
is($read_mask, $expected_mask, "check the mask");
}
ok($base->write(data => \$data, type => 'ico'),
"write grayscale paletted");
my $read = Imager->new;
- ok($read->read(data => $data), "read it back")
+ ok($read->read(data => $data, ico_masked => 0), "read it back")
or print "# ", $read->errstr, "\n";
is($read->type, 'paletted', "check type");
is($read->getchannels, 3, "check channels");
is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
"check the image");
}
+
+{
+ # check default mask processing
+ #
+ # the query at http://www.cpanforum.com/threads/5958 made it fairly
+ # obvious that the way Imager handled the mask in 0.59 was confusing
+ # when compared with loading other images with some sort of
+ # secondary alpha channel (eg. gif)
+ # So from 0.60 the mask is applied as an alpha channel by default.
+ # make sure that application works.
+
+ # the strange mask checks the optimization paths of the mask application
+ my $mask = <<EOS;
+01
+1001
+1100
+0011
+0000
+0010
+EOS
+ chomp $mask;
+ my $im = Imager->new(xsize => 4, ysize => 5, type => 'paletted');
+ $im->addcolors(colors => [ '#FF0000' ]);
+ $im->box(filled => 1, color => '#FF0000');
+ $im->settag(name => 'ico_mask', value => $mask);
+ my $imcopy = $im->convert(preset=>'addalpha');
+ my $red_alpha = Imager::Color->new(255, 0, 0, 0);
+ $imcopy->setpixel( 'x' => [ qw/0 3 0 1 2 3 2/ ],
+ 'y' => [ qw/0 0 1 1 2 2 4/ ],
+ color => $red_alpha);
+ my $data;
+ ok($im->write(data => \$data, type => 'ico'),
+ "save icon + mask");
+ my $im2 = Imager->new;
+ ok($im2->read(data => $data), "read ico with defaults");
+ is($im2->type, 'direct', 'expect a direct image');
+ is_image($im2, $imcopy, 'check against expected');
+}