From: Tony Cook Date: Tue, 1 Apr 2008 08:15:22 +0000 (+0000) Subject: - writing a 2 or 4 channel image to a PGM/PPM will now write that X-Git-Tag: Imager-0.63~8 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/fa90de94280f15f94fb7af88267f5aacc2400d3e - writing a 2 or 4 channel image to a PGM/PPM will now write that image as if composited against a background, black by default, overridable with the i_background tag/parameter. http://rt.cpan.org/Ticket/Display.html?id=30074 --- diff --git a/Changes b/Changes index f9086cff..8eefac5a 100644 --- a/Changes +++ b/Changes @@ -46,6 +46,11 @@ Imager 0.63 - unreleased with the i_background tag/parameter. https://rt.cpan.org/Ticket/Display.html?id=29876 + - writing a 2 or 4 channel image to a PGM/PPM will now write that + image as if composited against a background, black by default, + overridable with the i_background tag/parameter. + http://rt.cpan.org/Ticket/Display.html?id=30074 + Bug fixes: - Imager::Matrix2d->translate() now only requires one of the x or y diff --git a/freetyp2.c b/freetyp2.c index b3eccef2..d2e3c069 100644 --- a/freetyp2.c +++ b/freetyp2.c @@ -47,7 +47,7 @@ Truetype, Type1 and Windows FNT. static void ft2_push_message(int code); -static ft2_initialized = 0; +static int ft2_initialized = 0; static FT_Library library; /* diff --git a/image.c b/image.c index 79230fc5..27382f66 100644 --- a/image.c +++ b/image.c @@ -2453,6 +2453,30 @@ i_get_file_background(i_img *im, i_color *bg) { bg->channel[3] = 255; } +/* +=item i_get_file_backgroundf(im, &bg) + +Retrieve the file write background color tag from the image as a +floating point color. + +Implemented in terms of i_get_file_background(). + +If not present, returns black. + +=cut +*/ + +void +i_get_file_backgroundf(i_img *im, i_fcolor *fbg) { + i_color bg; + + i_get_file_background(im, &bg); + fbg->rgba.r = Sample8ToF(bg.rgba.r); + fbg->rgba.g = Sample8ToF(bg.rgba.g); + fbg->rgba.b = Sample8ToF(bg.rgba.b); + fbg->rgba.a = 1.0; +} + /* =back diff --git a/imager.h b/imager.h index b9abc818..24fd57ec 100644 --- a/imager.h +++ b/imager.h @@ -370,6 +370,7 @@ extern i_img *i_img_double_new(int x, int y, int ch); extern int i_img_is_monochrome(i_img *im, int *zero_is_white); extern void i_get_file_background(i_img *im, i_color *bg); +extern void i_get_file_backgroundf(i_img *im, i_fcolor *bg); const char * i_test_format_probe(io_glue *data, int length); @@ -591,4 +592,11 @@ extern void i_adapt_fcolors(int dest_channels, int src_channels, i_fcolor *colors, size_t count); +extern void +i_adapt_colors_bg(int dest_channels, int src_channels, i_color *colors, + size_t count, i_color const *bg); +extern void +i_adapt_fcolors_bg(int dest_channels, int src_channels, i_fcolor *colors, + size_t count, i_fcolor const *bg); + #endif diff --git a/pnm.c b/pnm.c index 3c96a56f..609fbbc6 100644 --- a/pnm.c +++ b/pnm.c @@ -751,49 +751,73 @@ write_pbm(i_img *im, io_glue *ig, int zero_is_white) { static int -write_ppm_data_8(i_img *im, io_glue *ig) { - int write_size = im->xsize * im->channels; +write_ppm_data_8(i_img *im, io_glue *ig, int want_channels) { + int write_size = im->xsize * want_channels; + i_color *line_buf = mymalloc(sizeof(i_color) * im->xsize); unsigned char *data = mymalloc(write_size); int y = 0; + int x; + int ch; int rc = 1; + i_color bg; + i_get_file_background(im, &bg); while (y < im->ysize && rc >= 0) { - i_gsamp(im, 0, im->xsize, y, data, NULL, im->channels); + i_color *linep = line_buf; + unsigned char *datap = data; + + i_glin(im, 0, im->xsize, y, line_buf); + i_adapt_colors_bg(want_channels, im->channels, line_buf, im->xsize, &bg); + for (x = 0; x < im->xsize; ++x) { + for (ch = 0; ch < want_channels; ++ch) { + *datap++ = linep->channel[ch]; + } + ++linep; + } if (i_io_write(ig, data, write_size) != write_size) { i_push_error(errno, "could not write ppm data"); + myfree(data); + myfree(line_buf); rc = 0; break; } ++y; } myfree(data); + myfree(line_buf); return rc; } static int -write_ppm_data_16(i_img *im, io_glue *ig) { - int sample_count = im->channels * im->xsize; +write_ppm_data_16(i_img *im, io_glue *ig, int want_channels) { + int sample_count = want_channels * im->xsize; int write_size = sample_count * 2; - int line_size = sample_count * sizeof(i_fsample_t); - i_fsample_t *line_buf = mymalloc(line_size); - i_fsample_t *samplep; + i_fcolor *line_buf = mymalloc(sizeof(i_fcolor) * im->xsize); unsigned char *write_buf = mymalloc(write_size); - unsigned char *writep; - int sample_num; int y = 0; + int x, ch; int rc = 1; + i_fcolor bg; + + i_get_file_backgroundf(im, &bg); while (y < im->ysize) { - i_gsampf(im, 0, im->xsize, y, line_buf, NULL, im->channels); - samplep = line_buf; - writep = write_buf; - for (sample_num = 0; sample_num < sample_count; ++sample_num) { - unsigned sample16 = SampleFTo16(*samplep++); - *writep++ = sample16 >> 8; - *writep++ = sample16 & 0xFF; + i_fcolor *linep = line_buf; + unsigned char *writep = write_buf; + + i_glinf(im, 0, im->xsize, y, line_buf); + i_adapt_fcolors_bg(want_channels, im->channels, line_buf, im->xsize, &bg); + for (x = 0; x < im->xsize; ++x) { + for (ch = 0; ch < want_channels; ++ch) { + unsigned sample16 = SampleFTo16(linep->channel[ch]); + *writep++ = sample16 >> 8; + *writep++ = sample16 & 0xFF; + } + ++linep; } + if (i_io_write(ig, write_buf, write_size) != write_size) { i_push_error(errno, "could not write ppm data"); rc = 0; @@ -827,14 +851,18 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { else { int type; int maxval; + int want_channels = im->channels; + + if (want_channels == 2 || want_channels == 4) + --want_channels; if (!i_tags_get_int(&im->tags, "pnm_write_wide_data", 0, &wide_data)) wide_data = 0; - if (im->channels == 3) { + if (want_channels == 3) { type = 6; } - else if (im->channels == 1) { + else if (want_channels == 1) { type = 5; } else { @@ -856,18 +884,19 @@ i_writeppm_wiol(i_img *im, io_glue *ig) { return(0); } - if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) { + if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type + && im->channels == want_channels) { if (ig->writecb(ig,im->idata,im->bytes) != im->bytes) { i_push_error(errno, "could not write ppm data"); return 0; } } else if (maxval == 255) { - if (!write_ppm_data_8(im, ig)) + if (!write_ppm_data_8(im, ig, want_channels)) return 0; } else { - if (!write_ppm_data_16(im, ig)) + if (!write_ppm_data_16(im, ig, want_channels)) return 0; } } diff --git a/t/t104ppm.t b/t/t104ppm.t index 7f2bec38..fd795e64 100644 --- a/t/t104ppm.t +++ b/t/t104ppm.t @@ -1,6 +1,6 @@ #!perl -w use Imager ':all'; -use Test::More tests => 153; +use Test::More tests => 173; use strict; use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image); @@ -209,17 +209,76 @@ is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag"); } { # check error messages set correctly - my $im = Imager->new(xsize=>100, ysize=>100, channels=>4); - ok(!$im->write(file=>"testout/t104_fail.ppm", type=>'pnm'), - "should fail to write 4 channel image"); - is($im->errstr, 'can only save 1 or 3 channel images to pnm', - "check error message"); + my $im = Imager->new; ok(!$im->read(file=>'t/t104ppm.t', type=>'pnm'), 'should fail to read script as an image file'); is($im->errstr, 'unable to read pnm image: bad header magic, not a PNM file', "check error message"); } +{ + # RT #30074 + # give 4/2 channel images a background color when saving to pnm + my $im = Imager->new(xsize=>16, ysize=>16, channels=>4); + $im->box(filled => 1, xmin => 8, color => '#FFE0C0'); + $im->box(filled => 1, color => NC(0, 192, 192, 128), + ymin => 8, xmax => 7); + ok($im->write(file=>"testout/t104_alpha.ppm", type=>'pnm'), + "should succeed writing 4 channel image"); + my $imread = Imager->new; + ok($imread->read(file => 'testout/t104_alpha.ppm'), "read it back"); + is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, + "check transparent became black"); + is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192, + "check color came through"); + is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96, + "check translucent came through"); + my $data; + ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000'), + "write with red background"); + ok($imread->read(data => $data, type => 'pnm'), + "read it back"); + is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, + "check transparent became red"); + is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192, + "check color came through"); + is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96, + "check translucent came through"); +} + +{ + # more RT #30074 - 16 bit images + my $im = Imager->new(xsize=>16, ysize=>16, channels=>4, bits => 16); + $im->box(filled => 1, xmin => 8, color => '#FFE0C0'); + $im->box(filled => 1, color => NC(0, 192, 192, 128), + ymin => 8, xmax => 7); + ok($im->write(file=>"testout/t104_alp16.ppm", type=>'pnm', + pnm_write_wide_data => 1), + "should succeed writing 4 channel image"); + my $imread = Imager->new; + ok($imread->read(file => 'testout/t104_alp16.ppm'), "read it back"); + is($imread->bits, 16, "check we did produce a 16 bit image"); + is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, + "check transparent became black"); + is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192, + "check color came through"); + is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96, + "check translucent came through"); + my $data; + ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000', + pnm_write_wide_data => 1), + "write with red background"); + ok($imread->read(data => $data, type => 'pnm'), + "read it back"); + is($imread->bits, 16, "check it's 16-bit"); + is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, + "check transparent became red"); + is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192, + "check color came through"); + is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96, + "check translucent came through"); +} + # various bad input files print "# check error handling\n"; {