- writing a 2 or 4 channel image to a PGM/PPM will now write that
authorTony Cook <tony@develop=help.com>
Tue, 1 Apr 2008 08:15:22 +0000 (08:15 +0000)
committerTony Cook <tony@develop=help.com>
Tue, 1 Apr 2008 08:15:22 +0000 (08:15 +0000)
   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

Changes
freetyp2.c
image.c
imager.h
pnm.c
t/t104ppm.t

diff --git a/Changes b/Changes
index f9086cf..8eefac5 100644 (file)
--- 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
index b3eccef..d2e3c06 100644 (file)
@@ -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 79230fc..27382f6 100644 (file)
--- 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
 
index b9abc81..24fd57e 100644 (file)
--- 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 3c96a56..609fbbc 100644 (file)
--- 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;
     }
   }
index 7f2bec3..fd795e6 100644 (file)
@@ -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";
 {