]> git.imager.perl.org - imager.git/commitdiff
- Finished/rewrote Arnar's old SGI RGB file format support, so Imager
authorTony Cook <tony@develop=help.com>
Tue, 31 Jul 2007 12:19:49 +0000 (12:19 +0000)
committerTony Cook <tony@develop=help.com>
Tue, 31 Jul 2007 12:19:49 +0000 (12:19 +0000)
   now has full SGI RGB image format, including RLE and 16-bit/sample
   images.
   https://rt.cpan.org/Ticket/Display.html?id=8666

 - logging functions are now available in the API

 - the convert() method now returns an image of the same sample size as
   the source image.
   https://rt.cpan.org/Ticket/Display.html?id=28492

37 files changed:
Changes
Imager.pm
Imager.xs
MANIFEST
Makefile.PL
SGI/Makefile.PL [new file with mode: 0644]
SGI/SGI.pm [new file with mode: 0644]
SGI/SGI.xs [new file with mode: 0644]
SGI/imsgi.c [new file with mode: 0644]
SGI/imsgi.h [new file with mode: 0644]
SGI/t/00load.t [new file with mode: 0644]
SGI/t/10read.t [new file with mode: 0644]
SGI/t/20write.t [new file with mode: 0644]
SGI/testimg/rle.rgb [new file with mode: 0644]
SGI/testimg/rle12.rgb [new file with mode: 0644]
SGI/testimg/rle16.rgb [new file with mode: 0644]
SGI/testimg/rle6.rgb [new file with mode: 0644]
SGI/testimg/rleagr.rgb [new file with mode: 0644]
SGI/testimg/verb.rgb [new file with mode: 0644]
SGI/testimg/verb12.rgb [new file with mode: 0644]
SGI/testimg/verb16.rgb [new file with mode: 0644]
SGI/testimg/verb6.rgb [new file with mode: 0644]
convert.c
feat.h
image.c
imager.h
imdatatypes.h
imext.c
imext.h
imexttypes.h
imio.h
lib/Imager/APIRef.pod
lib/Imager/Files.pod
lib/Imager/Test.pm
rgb.c [deleted file]
t/t1000files.t
t/t67convert.t

diff --git a/Changes b/Changes
index 5eae80d05e28e3b64a0d8b93befee66dde44873a..2efa15f828a6cab12ba95d575656cbde49fd5206 100644 (file)
--- a/Changes
+++ b/Changes
@@ -3,6 +3,13 @@ Imager release history.  Older releases can be found in Changes.old
 Imager 0.60
 ===========
 
 Imager 0.60
 ===========
 
+ - Finished/rewrote Arnar's old SGI RGB file format support, so Imager
+   now has full SGI RGB image format, including RLE and 16-bit/sample
+   images.
+   https://rt.cpan.org/Ticket/Display.html?id=8666
+
+ - logging functions are now available in the API
+
 Bug fixes:
 
  - in some cases it's possible for giflib/libungif to return color 
 Bug fixes:
 
  - in some cases it's possible for giflib/libungif to return color 
@@ -18,6 +25,10 @@ Bug fixes:
    and i_glinf() API functions which continue to not set the unused
    channels.
 
    and i_glinf() API functions which continue to not set the unused
    channels.
 
+ - the convert() method now returns an image of the same sample size as
+   the source image.
+   https://rt.cpan.org/Ticket/Display.html?id=28492
+
 Imager 0.59 - 14 June 2007
 ===========
 
 Imager 0.59 - 14 June 2007
 ===========
 
index b6131c9633b15c666f75158478b919c78cb0b7ad..28a72bf98016e1f7679dabfdef9f18e6f7a9c057 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -1378,16 +1378,6 @@ sub read {
     $self->{DEBUG} && print "loading a tga file\n";
   }
 
     $self->{DEBUG} && print "loading a tga file\n";
   }
 
-  if ( $input{'type'} eq 'rgb' ) {
-    $self->{IMG}=i_readrgb_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
-    if ( !defined($self->{IMG}) ) {
-      $self->{ERRSTR}=$self->_error_as_msg();
-      return undef;
-    }
-    $self->{DEBUG} && print "loading a tga file\n";
-  }
-
-
   if ( $input{'type'} eq 'raw' ) {
     my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
 
   if ( $input{'type'} eq 'raw' ) {
     my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
 
@@ -3155,9 +3145,9 @@ sub convert {
     $matrix = $opts{matrix};
   }
 
     $matrix = $opts{matrix};
   }
 
-  my $new = Imager->new();
-  $new->{IMG} = i_img_new();
-  unless (i_convert($new->{IMG}, $self->{IMG}, $matrix)) {
+  my $new = Imager->new;
+  $new->{IMG} = i_convert($self->{IMG}, $matrix);
+  unless ($new->{IMG}) {
     # most likely a bad matrix
     $self->{ERRSTR} = _error_as_msg();
     return undef;
     # most likely a bad matrix
     $self->{ERRSTR} = _error_as_msg();
     return undef;
@@ -3409,7 +3399,7 @@ sub def_guess_type {
   return 'png'  if ($ext eq "png");
   return 'bmp'  if ($ext eq "bmp" || $ext eq "dib");
   return 'tga'  if ($ext eq "tga");
   return 'png'  if ($ext eq "png");
   return 'bmp'  if ($ext eq "bmp" || $ext eq "dib");
   return 'tga'  if ($ext eq "tga");
-  return 'rgb'  if ($ext eq "rgb");
+  return 'sgi'  if ($ext eq "rgb" || $ext eq "bw" || $ext eq "sgi" || $ext eq "rgba");
   return 'gif'  if ($ext eq "gif");
   return 'raw'  if ($ext eq "raw");
   return lc $ext; # best guess
   return 'gif'  if ($ext eq "gif");
   return 'raw'  if ($ext eq "raw");
   return lc $ext; # best guess
@@ -3888,6 +3878,8 @@ convolution - L<Imager::Filter/conv>
 
 cropping - L<Imager::Transformations/crop>
 
 
 cropping - L<Imager::Transformations/crop>
 
+CUR files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
 C<diff> images - L<Imager::Filter/"Image Difference">
 
 dpi - L<Imager::ImageTypes/i_xres>, 
 C<diff> images - L<Imager::Filter/"Image Difference">
 
 dpi - L<Imager::ImageTypes/i_xres>, 
@@ -3938,6 +3930,8 @@ guassian blur - L<Imager::Filter/guassian>
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
 
 hatch fills - L<Imager::Fill/"Hatched fills">
 
+ICO files - L<Imager::Files/"ICO (Microsoft Windows Icon) and CUR (Microsoft Windows Cursor)">
+
 invert image - L<Imager::Filter/hardinvert>
 
 JPEG - L<Imager::Files/"JPEG">
 invert image - L<Imager::Filter/hardinvert>
 
 JPEG - L<Imager::Files/"JPEG">
@@ -3976,10 +3970,14 @@ rectangles, drawing - L<Imager::Draw/box>
 resizing an image - L<Imager::Transformations/scale>, 
 L<Imager::Transformations/crop>
 
 resizing an image - L<Imager::Transformations/scale>, 
 L<Imager::Transformations/crop>
 
+RGB (SGI) files - L<Imager::Files/"SGI (RGB, BW)">
+
 saving an image - L<Imager::Files>
 
 scaling - L<Imager::Transformations/scale>
 
 saving an image - L<Imager::Files>
 
 scaling - L<Imager::Transformations/scale>
 
+SGI files - L<Imager::Files/"SGI (RGB, BW)">
+
 sharpen - L<Imager::Filters/unsharpmask>, L<Imager::Filters/conv>
 
 size, image - L<Imager::ImageTypes/getwidth>,
 sharpen - L<Imager::Filters/unsharpmask>, L<Imager::Filters/conv>
 
 size, image - L<Imager::ImageTypes/getwidth>,
index 46f7b3e37f72ed98544d292e48eaee4f0e431a58..80d65a6039f2b8110e7194ddb160d1d11840087d 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -1731,23 +1731,19 @@ i_conv(im,pcoef)
             i_conv(im,coeff,len);
             myfree(coeff);
 
             i_conv(im,coeff,len);
             myfree(coeff);
 
-undef_int
-i_convert(im, src, coeff)
-    Imager::ImgRaw     im
+Imager::ImgRaw
+i_convert(src, avmain)
     Imager::ImgRaw     src
     Imager::ImgRaw     src
+    AV *avmain
        PREINIT:
          float *coeff;
          int outchan;
          int inchan;
        PREINIT:
          float *coeff;
          int outchan;
          int inchan;
-         AV *avmain;
           SV **temp;
           AV *avsub;
          int len;
          int i, j;
         CODE:
           SV **temp;
           AV *avsub;
          int len;
          int i, j;
         CODE:
-         if (!SvROK(ST(2)) || SvTYPE(SvRV(ST(2))) != SVt_PVAV)
-           croak("i_convert: parameter 3 must be an arrayref\n");
-          avmain = (AV*)SvRV(ST(2));
          outchan = av_len(avmain)+1;
           /* find the biggest */
           inchan = 0;
          outchan = av_len(avmain)+1;
           /* find the biggest */
           inchan = 0;
@@ -1774,7 +1770,7 @@ i_convert(im, src, coeff)
            while (i < inchan)
              coeff[i++ + j*inchan] = 0;
          }
            while (i < inchan)
              coeff[i++ + j*inchan] = 0;
          }
-         RETVAL = i_convert(im, src, coeff, outchan, inchan);
+         RETVAL = i_convert(src, coeff, outchan, inchan);
           myfree(coeff);
        OUTPUT:
          RETVAL
           myfree(coeff);
        OUTPUT:
          RETVAL
@@ -2941,27 +2937,6 @@ i_readtga_wiol(ig, length)
                int     length
 
 
                int     length
 
 
-undef_int
-i_writergb_wiol(im,ig, wierdpack, compress, idstring)
-    Imager::ImgRaw     im
-        Imager::IO     ig
-               int     wierdpack
-               int     compress
-              char*    idstring
-            PREINIT:
-                int idlen;
-              CODE:
-                idlen  = SvCUR(ST(4));
-                RETVAL = i_writergb_wiol(im, ig, wierdpack, compress, idstring, idlen);
-                OUTPUT:
-                RETVAL
-
-
-Imager::ImgRaw
-i_readrgb_wiol(ig, length)
-        Imager::IO     ig
-               int     length
-
 
 
 Imager::ImgRaw
 
 
 Imager::ImgRaw
index 1ecc842c1624afd75f1f47ee4b186b7eb54a04ce..1fd5f9c030417e3785733725b05afa1de40b7f22 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -54,6 +54,13 @@ Mandelbrot/mandel.c
 Mandelbrot/t/t00mandel.t
 Makefile.PL
 README
 Mandelbrot/t/t00mandel.t
 Makefile.PL
 README
+SGI/Makefile.PL
+SGI/SGI.pm
+SGI/SGI.xs
+SGI/imsgi.c
+SGI/t/00load.t
+SGI/t/10read.t
+SGI/t/20write.t
 apidocs.perl    Build lib/Imager/APIRef.pm
 bigtest.perl   Library selection tester
 bmp.c           Reading and writing Windows BMP files
 apidocs.perl    Build lib/Imager/APIRef.pm
 bigtest.perl   Library selection tester
 bmp.c           Reading and writing Windows BMP files
@@ -177,7 +184,6 @@ regmach.h
 regops.perl
 render.im
 rendert.h      Buffer rendering engine types
 regops.perl
 render.im
 rendert.h      Buffer rendering engine types
-rgb.c           Reading and writing SGI rgb files
 rotate.c
 rubthru.im
 samples/README
 rotate.c
 rubthru.im
 samples/README
index 56b0852f7a95d66c764d53ae84e8b39458dfbf0b..a47d8e9f98520ff5c2bdba21f74a1c36f117dafc 100644 (file)
@@ -155,7 +155,7 @@ my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
               filters.o dynaload.o stackmach.o datatypes.o
               regmach.o trans2.o quant.o error.o convert.o
               map.o tags.o palimg.o maskimg.o img16.o rotate.o
               filters.o dynaload.o stackmach.o datatypes.o
               regmach.o trans2.o quant.o error.o convert.o
               map.o tags.o palimg.o maskimg.o img16.o rotate.o
-              bmp.o tga.o rgb.o color.o fills.o imgdouble.o limits.o hlines.o
+              bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
               imext.o scale.o rubthru.o render.o);
 
 $Recommends{Imager} =
               imext.o scale.o rubthru.o render.o);
 
 $Recommends{Imager} =
diff --git a/SGI/Makefile.PL b/SGI/Makefile.PL
new file mode 100644 (file)
index 0000000..56957aa
--- /dev/null
@@ -0,0 +1,20 @@
+#!perl -w
+use strict;
+use ExtUtils::MakeMaker;
+
+my %opts = 
+  (
+   NAME => 'Imager::File::SGI',
+   VERSION_FROM => 'SGI.pm',
+   OBJECT => 'SGI.o imsgi.o',
+   INC => '-I..'
+  );
+my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
+if ($MM_ver > 6.06) {
+  $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+  $opts{ABSTRACT} = 'SGI Image file support';
+}
+
+WriteMakefile(%opts);
+
+
diff --git a/SGI/SGI.pm b/SGI/SGI.pm
new file mode 100644 (file)
index 0000000..67cc6a9
--- /dev/null
@@ -0,0 +1,89 @@
+package Imager::File::SGI;
+use strict;
+use Imager;
+use vars qw($VERSION @ISA);
+
+BEGIN {
+  $VERSION = "0.01";
+  
+  eval {
+    require XSLoader;
+    XSLoader::load('Imager::File::SGI', $VERSION);
+    1;
+  } or do {
+    require DynaLoader;
+    push @ISA, 'DynaLoader';
+    bootstrap Imager::File::SGI $VERSION;
+  };
+}
+
+Imager->register_reader
+  (
+   type=>'sgi',
+   single => 
+   sub { 
+     my ($im, $io, %hsh) = @_;
+     $im->{IMG} = i_readsgi_wiol($io, $hsh{page} || 0);
+
+     unless ($im->{IMG}) {
+       $im->_set_error(Imager->_error_as_msg);
+       return;
+     }
+     return $im;
+   },
+  );
+
+Imager->register_writer
+  (
+   type=>'sgi',
+   single => 
+   sub { 
+     my ($im, $io, %hsh) = @_;
+
+     $im->_set_opts(\%hsh, "i_", $im);
+     $im->_set_opts(\%hsh, "sgi_", $im);
+
+     unless (i_writesgi_wiol($io, $im->{IMG})) {
+       $im->_set_error(Imager->_error_as_msg);
+       return;
+     }
+     return $im;
+   },
+  );
+
+__END__
+
+=head1 NAME
+
+Imager::File::ICO - read MS Icon files
+
+=head1 SYNOPSIS
+
+  use Imager;
+
+  my $img = Imager->new;
+  $img->read(file=>"foo.ico")
+    or die $img->errstr;
+
+  my @imgs = Imager->read_multi(file => "foo.ico")
+    or die Imager->errstr;
+
+  $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.
+
+=cut
diff --git a/SGI/SGI.xs b/SGI/SGI.xs
new file mode 100644 (file)
index 0000000..d238180
--- /dev/null
@@ -0,0 +1,26 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "imext.h"
+#include "imperl.h"
+#include "imsgi.h"
+#include "ppport.h"
+
+DEFINE_IMAGER_CALLBACKS;
+
+MODULE = Imager::File::SGI  PACKAGE = Imager::File::SGI
+
+PROTOTYPES: DISABLE
+
+Imager::ImgRaw
+i_readsgi_wiol(ig, partial)
+       Imager::IO ig
+       int partial
+
+int
+i_writesgi_wiol(ig, im)
+       Imager::IO ig
+       Imager::ImgRaw im
+
+BOOT:
+       PERL_INITIALIZE_IMAGER_CALLBACKS;
diff --git a/SGI/imsgi.c b/SGI/imsgi.c
new file mode 100644 (file)
index 0000000..afc4d87
--- /dev/null
@@ -0,0 +1,1200 @@
+#include "imsgi.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+/* value for imagic */
+#define SGI_MAGIC 474
+
+/* values for the storage field */
+#define SGI_STORAGE_VERBATIM 0
+#define SGI_STORAGE_RLE 1
+
+/* values for the colormap field */
+#define SGI_COLORMAP_NORMAL 0
+#define SGI_COLORMAP_DITHERED 1
+#define SGI_COLORMAP_SCREEN 2
+#define SGI_COLORMAP_COLORMAP 3
+
+/* we add that little bit to avoid rounding issues */
+#define SampleFTo16(num) ((int)((num) * 65535.0 + 0.01))
+
+typedef struct {
+  unsigned short imagic;
+  unsigned char storagetype;
+  unsigned char BPC;
+  unsigned short dimensions;
+  unsigned short xsize, ysize, zsize;
+  unsigned int pixmin, pixmax;
+  char name[80];
+  unsigned int colormap;
+} rgb_header;
+
+static i_img *
+read_rgb_8_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
+static i_img *
+read_rgb_8_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
+static i_img *
+read_rgb_16_verbatim(i_img *im, io_glue *ig, rgb_header const *hdr);
+static i_img *
+read_rgb_16_rle(i_img *im, io_glue *ig, rgb_header const *hdr);
+static int
+write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2);
+static int
+write_sgi_8_rle(i_img *img, io_glue *ig);
+static int
+write_sgi_8_verb(i_img *img, io_glue *ig);
+static int
+write_sgi_16_rle(i_img *img, io_glue *ig);
+static int
+write_sgi_16_verb(i_img *img, io_glue *ig);
+
+#define Sample16ToF(num) ((num) / 65535.0)
+
+#define _STRING(x) #x
+#define STRING(x) _STRING(x)
+
+/*
+=head1 NAME
+
+rgb.c - implements reading and writing sgi image files, uses io layer.
+
+=head1 SYNOPSIS
+
+   io_glue *ig = io_new_fd( fd );
+   i_img *im   = i_readrgb_wiol(ig, 0); // disallow partial reads
+   // or 
+   io_glue *ig = io_new_fd( fd );
+   return_code = i_writergb_wiol(im, ig); 
+
+=head1 DESCRIPTION
+
+imsgi.c implements the basic functions to read and write portable SGI
+files.  It uses the iolayer and needs either a seekable source or an
+entire memory mapped buffer.
+
+=head1 FUNCTION REFERENCE
+
+Some of these functions are internal.
+
+=over
+
+=cut
+*/
+
+/*
+=item rgb_header_unpack(header, headbuf)
+
+Unpacks the header structure into from buffer and stores
+in the header structure.
+
+    header - header structure
+    headbuf - buffer to unpack from
+
+=cut
+*/
+
+
+static
+void
+rgb_header_unpack(rgb_header *header, const unsigned char *headbuf) {
+  header->imagic      = (headbuf[0]<<8) + headbuf[1];
+  header->storagetype = headbuf[2];
+  header->BPC         = headbuf[3];
+  header->dimensions  = (headbuf[4]<<8) + headbuf[5];
+  header->xsize       = (headbuf[6]<<8) + headbuf[7];
+  header->ysize       = (headbuf[8]<<8) + headbuf[9];
+  header->zsize       = (headbuf[10]<<8) + headbuf[11];
+  header->pixmin      = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15];
+  header->pixmax      = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19];
+  memcpy(header->name,headbuf+24,80);
+  header->name[79] = '\0';
+  header->colormap    = (headbuf[104]<<24) + (headbuf[105]<<16)+(headbuf[106]<<8)+headbuf[107];
+}
+
+/* don't make this a macro */
+static void
+store_16(unsigned char *buf, unsigned short value) {
+  buf[0] = value >> 8;
+  buf[1] = value & 0xFF;
+}
+
+static void
+store_32(unsigned char *buf, unsigned short value) {
+  buf[0] = value >> 24;
+  buf[1] = (value >> 16) & 0xFF;
+  buf[2] = (value >> 8) & 0xFF;
+  buf[3] = value & 0xFF;
+}
+
+/*
+=item rgb_header_pack(header, headbuf)
+
+Packs header structure into buffer for writing.
+
+    header - header structure
+    headbuf - buffer to pack into
+
+=cut
+*/
+
+static
+void
+rgb_header_pack(const rgb_header *header, unsigned char headbuf[512]) {
+  memset(headbuf, 0, 512);
+  store_16(headbuf, header->imagic);
+  headbuf[2] = header->storagetype;
+  headbuf[3] = header->BPC;
+  store_16(headbuf+4, header->dimensions);
+  store_16(headbuf+6, header->xsize);
+  store_16(headbuf+8, header->ysize);
+  store_16(headbuf+10, header->zsize);
+  store_32(headbuf+12, header->pixmin);
+  store_32(headbuf+16, header->pixmax);
+  memccpy(headbuf+24, header->name, '\0', 80);
+  store_32(headbuf+104, header->colormap);
+}
+
+/*
+=item i_readsgi_wiol(ig, partial)
+
+Read in an image from the iolayer data source and return the image structure to it.
+Returns NULL on error.
+
+   ig     - io_glue object
+   length - maximum length to read from data source, before closing it -1 
+            signifies no limit.
+
+=cut
+*/
+
+i_img *
+i_readsgi_wiol(io_glue *ig, int partial) {
+  i_img *img = NULL;
+  int width, height, channels;
+  rgb_header header;
+  unsigned char headbuf[512];
+
+  mm_log((1,"i_readsgi(ig %p, partial %d)\n", ig, partial));
+  i_clear_error();
+
+  if (ig->readcb(ig, headbuf, 512) != 512) {
+    i_push_error(errno, "SGI image: could not read header");
+    return NULL;
+  }
+
+  rgb_header_unpack(&header, headbuf);
+
+  if (header.imagic != SGI_MAGIC) {
+    i_push_error(0, "SGI image: invalid magic number");
+    return NULL;
+  }
+
+  mm_log((1,"imagic:         %d\n", header.imagic));
+  mm_log((1,"storagetype:    %d\n", header.storagetype));
+  mm_log((1,"BPC:            %d\n", header.BPC));
+  mm_log((1,"dimensions:     %d\n", header.dimensions));
+  mm_log((1,"xsize:          %d\n", header.xsize));
+  mm_log((1,"ysize:          %d\n", header.ysize));
+  mm_log((1,"zsize:          %d\n", header.zsize));
+  mm_log((1,"min:            %d\n", header.pixmin));
+  mm_log((1,"max:            %d\n", header.pixmax));
+  mm_log((1,"name [skipped]\n"));
+  mm_log((1,"colormap:       %d\n", header.colormap));
+
+  if (header.colormap != SGI_COLORMAP_NORMAL) {
+    i_push_errorf(0, "SGI image: invalid value for colormap (%d)", header.colormap);
+    return NULL;
+  }
+
+  if (header.BPC != 1 && header.BPC != 2) {
+    i_push_errorf(0, "SGI image: invalid value for BPC (%d)", header.BPC);
+    return NULL;
+  }
+
+  if (header.storagetype != SGI_STORAGE_VERBATIM 
+      && header.storagetype != SGI_STORAGE_RLE) {
+    i_push_error(0, "SGI image: invalid storage type field");
+    return NULL;
+  }
+
+  if (header.pixmin >= header.pixmax) {
+    i_push_error(0, "SGI image: invalid pixmin >= pixmax");
+    return NULL;
+  }
+
+  width    = header.xsize;
+  height   = header.ysize;
+  channels = header.zsize;
+
+  switch (header.dimensions) {
+  case 1:
+    channels = 1;
+    height = 1;
+    break;
+
+  case 2:
+    channels = 1;
+    break;
+
+  case 3:
+    /* fall through and use all of the dimensions */
+    break;
+
+  default:
+    i_push_error(0, "SGI image: invalid dimension field");
+    return NULL;
+  }
+
+  if (!i_int_check_image_file_limits(width, height, channels, header.BPC)) {
+    mm_log((1, "i_readsgi_wiol: image size exceeds limits\n"));
+    return NULL;
+  }
+
+  if (header.BPC == 1) {
+    img = i_img_8_new(width, height, channels);
+    if (!img)
+      goto ErrorReturn;
+
+    switch (header.storagetype) {
+    case SGI_STORAGE_VERBATIM:
+      img = read_rgb_8_verbatim(img, ig, &header);
+      break;
+
+    case SGI_STORAGE_RLE:
+      img = read_rgb_8_rle(img, ig, &header);
+      break;
+
+    default:
+      goto ErrorReturn;
+    }
+  }
+  else {
+    img = i_img_16_new(width, height, channels);
+    if (!img)
+      goto ErrorReturn;
+
+    switch (header.storagetype) {
+    case SGI_STORAGE_VERBATIM:
+      img = read_rgb_16_verbatim(img, ig, &header);
+      break;
+
+    case SGI_STORAGE_RLE:
+      img = read_rgb_16_rle(img, ig, &header);
+      break;
+
+    default:
+      goto ErrorReturn;
+    }
+  }
+
+  if (!img)
+    goto ErrorReturn;
+
+  if (*header.name)
+    i_tags_set(&img->tags, "i_comment", header.name, -1);
+  i_tags_setn(&img->tags, "sgi_pixmin", header.pixmin);
+  i_tags_setn(&img->tags, "sgi_pixmax", header.pixmax);
+  i_tags_setn(&img->tags, "sgi_bpc", header.BPC);
+  i_tags_setn(&img->tags, "sgi_rle", header.storagetype == SGI_STORAGE_RLE);
+  i_tags_set(&img->tags, "i_format", "sgi", -1);
+
+  return img;
+
+ ErrorReturn:
+  if (img) i_img_destroy(img);
+  return NULL;
+}
+
+/*
+=item i_writergb_wiol(img, ig)
+
+Writes an image in targa format.  Returns 0 on error.
+
+   img    - image to store
+   ig     - io_glue object
+
+=cut
+*/
+
+int
+i_writesgi_wiol(io_glue *ig, i_img *img) {
+  int rle;
+  int bpc2;
+
+  i_clear_error();
+
+  if (!write_sgi_header(img, ig, &rle, &bpc2))
+    return 0;
+
+  mm_log((1, "format rle %d bpc2 %d\n", rle, bpc2));
+
+  if (bpc2) {
+    if (rle)
+      return write_sgi_16_rle(img, ig);
+    else
+      return write_sgi_16_verb(img, ig);
+  }
+  else {
+    if (rle)
+      return write_sgi_8_rle(img, ig);
+    else
+      return write_sgi_8_verb(img, ig);
+  }
+}
+
+static i_img *
+read_rgb_8_verbatim(i_img *img, io_glue *ig, rgb_header const *header) {
+  i_color *linebuf;
+  unsigned char *databuf;
+  int c, y;
+  int savemask;
+  i_img_dim width = i_img_get_width(img);
+  i_img_dim height = i_img_get_height(img);
+  int channels = i_img_getchannels(img);
+  int pixmin = header->pixmin;
+  int pixmax = header->pixmax;
+  int outmax = pixmax - pixmin;
+  
+  linebuf   = mymalloc(width * sizeof(i_color)); /* checked 31Jul07 TonyC */
+  databuf   = mymalloc(width); /* checked 31Jul07 TonyC */
+
+  savemask = i_img_getmask(img);
+
+  for(c = 0; c < channels; c++) {
+    i_img_setmask(img, 1<<c);
+    for(y = 0; y < height; y++) {
+      int x;
+      
+      if (ig->readcb(ig, databuf, width) != width) {
+       i_push_error(0, "SGI image: cannot read image data");
+       i_img_destroy(img);
+       myfree(linebuf);
+       myfree(databuf);
+       return NULL;
+      }
+
+      if (pixmin == 0 && pixmax == 255) {
+       for(x = 0; x < img->xsize; x++)
+         linebuf[x].channel[c] = databuf[x];
+      }
+      else {
+       for(x = 0; x < img->xsize; x++) {
+         int sample = databuf[x];
+         if (sample < pixmin)
+           sample = 0;
+         else if (sample > pixmax)
+           sample = outmax;
+         else
+           sample -= pixmin;
+           
+         linebuf[x].channel[c] = sample * 255 / outmax;
+       }
+      }
+      
+      i_plin(img, 0, width, height-1-y, linebuf);
+    }
+  }
+  i_img_setmask(img, savemask);
+
+  myfree(linebuf);
+  myfree(databuf);
+  
+  return img;
+}
+
+static int
+read_rle_tables(io_glue *ig, i_img *img,
+               unsigned long **pstart_tab, unsigned long **plength_tab, 
+               unsigned long *pmax_length) {
+  i_img_dim height = i_img_get_height(img);
+  int channels = i_img_getchannels(img);
+  unsigned char *databuf;
+  unsigned long *start_tab, *length_tab;
+  unsigned long max_length = 0;
+  int i;
+  size_t databuf_size = (size_t)height * channels * 4;
+  size_t tab_size = (size_t)height * channels * sizeof(unsigned long);
+
+  /* assumption: that the lengths are in bytes rather than in pixels */
+  if (databuf_size / height / channels != 4
+      || tab_size / height / channels != sizeof(unsigned long)) {
+    i_push_error(0, "SGI image: integer overflow calculating allocation size");
+    return 0;
+  }
+  databuf    = mymalloc(height * channels * 4);  /* checked 31Jul07 TonyC */
+  start_tab  = mymalloc(height*channels*sizeof(unsigned long));
+  length_tab = mymalloc(height*channels*sizeof(unsigned long));
+    
+    /* Read offset table */
+  if (ig->readcb(ig, databuf, height * channels * 4) != height * channels * 4) {
+    i_push_error(0, "SGI image: short read reading RLE start table");
+    goto ErrorReturn;
+  }
+
+  for(i = 0; i < height * channels; i++) 
+    start_tab[i] = (databuf[i*4] << 24) | (databuf[i*4+1] << 16) | 
+      (databuf[i*4+2] << 8) | (databuf[i*4+3]);
+
+
+  /* Read length table */
+  if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) {
+    i_push_error(0, "SGI image: short read reading RLE length table");
+    goto ErrorReturn;
+  }
+
+  for(i=0; i < height * channels; i++) {
+    length_tab[i] = (databuf[i*4] << 24) + (databuf[i*4+1] << 16)+
+      (databuf[i*4+2] << 8) + (databuf[i*4+3]);
+    if (length_tab[i] > max_length)
+      max_length = length_tab[i];
+  }
+
+  mm_log((3, "Offset/length table:\n"));
+  for(i=0; i < height * channels; i++)
+    mm_log((3, "%d: %d/%d\n", i, start_tab[i], length_tab[i]));
+
+  *pstart_tab = start_tab;
+  *plength_tab = length_tab;
+  *pmax_length = max_length;
+
+  myfree(databuf);
+
+  return 1;
+
+ ErrorReturn:
+  myfree(databuf);
+  myfree(start_tab);
+  myfree(length_tab);
+
+  return 0;
+}
+
+static i_img *
+read_rgb_8_rle(i_img *img, io_glue *ig, rgb_header const *header) {
+  i_color *linebuf = NULL;
+  unsigned char *databuf = NULL;
+  unsigned long *start_tab, *length_tab;
+  unsigned long max_length;
+  i_img_dim width = i_img_get_width(img);
+  i_img_dim height = i_img_get_height(img);
+  int channels = i_img_getchannels(img);;
+  i_img_dim y;
+  int c;
+  int pixmin = header->pixmin;
+  int pixmax = header->pixmax;
+  int outmax = pixmax - pixmin;
+
+  if (!read_rle_tables(ig, img,  
+                      &start_tab, &length_tab, &max_length)) {
+    i_img_destroy(img);
+    return NULL;
+  }
+
+  mm_log((1, "maxlen for an rle buffer: %d\n", max_length));
+
+  if (max_length > (img->xsize + 1) * 2) {
+    i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length);
+    goto ErrorReturn;
+  }
+
+  linebuf = mymalloc(width*sizeof(i_color)); /* checked 31Jul07 TonyC */
+  databuf = mymalloc(max_length); /* checked 31Jul07 TonyC */
+
+  for(y = 0; y < img->ysize; y++) {
+    for(c = 0; c < channels; c++) {
+      int ci = height * c + y;
+      int datalen = length_tab[ci];
+      unsigned char *inp;
+      i_color *outp;
+      int data_left = datalen;
+      int pixels_left = width;
+      i_sample_t sample;
+      
+      if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
+       i_push_error(0, "SGI image: cannot seek to RLE data");
+       goto ErrorReturn;
+      }
+      if (ig->readcb(ig, databuf, datalen) != datalen) {
+       i_push_error(0, "SGI image: cannot read RLE data");
+       goto ErrorReturn;
+      }
+      
+      inp = databuf;
+      outp = linebuf;
+      while (data_left) {
+       int code = *inp++;
+       int count = code & 0x7f;
+       --data_left;
+
+       if (count == 0)
+         break;
+       if (code & 0x80) {
+         /* literal run */
+         /* sanity checks */
+         if (count > pixels_left) {
+           i_push_error(0, "SGI image: literal run overflows scanline");
+           goto ErrorReturn;
+         }
+         if (count > data_left) {
+           i_push_error(0, "SGI image: literal run consumes more data than available");
+           goto ErrorReturn;
+         }
+         /* copy the run */
+         pixels_left -= count;
+         data_left -= count;
+         if (pixmin == 0 && pixmax == 255) {
+           while (count-- > 0) {
+             outp->channel[c] = *inp++;
+             ++outp;
+           }
+         }
+         else {
+           while (count-- > 0) {
+             int sample = *inp++;
+             if (sample < pixmin)
+               sample = 0;
+             else if (sample > pixmax)
+               sample = outmax;
+             else
+               sample -= pixmin;
+             outp->channel[c] = sample * 255 / outmax;
+             ++outp;
+           }
+         }
+       }
+       else {
+         /* RLE run */
+         if (count > pixels_left) {
+           i_push_error(0, "SGI image: RLE run overflows scanline");
+           mm_log((2, "RLE run overflows scanline (y %d chan %d offset %ld len %ld)\n", y, c, start_tab[ci], length_tab[ci]));
+           goto ErrorReturn;
+         }
+         if (data_left < 1) {
+           i_push_error(0, "SGI image: RLE run has no data for pixel");
+           goto ErrorReturn;
+         }
+         sample = *inp++;
+         if (pixmin != 0 || pixmax != 255) {
+           if (sample < pixmin)
+             sample = 0;
+           else if (sample > pixmax)
+             sample = outmax;
+           else
+             sample -= pixmin;
+           sample = sample * 255 / outmax;
+         }
+         --data_left;
+         pixels_left -= count;
+         while (count-- > 0) {
+           outp->channel[c] = sample;
+           ++outp;
+         }
+       }
+      }
+      /* must have a full scanline */
+      if (pixels_left) {
+       i_push_error(0, "SGI image: incomplete RLE scanline");
+       goto ErrorReturn;
+      }
+      /* must have used all of the data */
+      if (data_left) {
+       i_push_errorf(0, "SGI image: unused RLE data");
+       goto ErrorReturn;
+      }
+    }
+    i_plin(img, 0, width, height-1-y, linebuf);
+  }
+
+  myfree(linebuf);
+  myfree(databuf);
+  myfree(start_tab);
+  myfree(length_tab);
+
+  return img;
+
+ ErrorReturn:
+  if (linebuf)
+    myfree(linebuf);
+  if (databuf)
+    myfree(databuf);
+  myfree(start_tab);
+  myfree(length_tab);
+  i_img_destroy(img);
+  return NULL;
+}
+
+static i_img *
+read_rgb_16_verbatim(i_img *img, io_glue *ig, rgb_header const *header) {
+  i_fcolor *linebuf;
+  unsigned char *databuf;
+  int c, y;
+  int savemask;
+  i_img_dim width = i_img_get_width(img);
+  i_img_dim height = i_img_get_height(img);
+  int channels = i_img_getchannels(img);
+  int pixmin = header->pixmin;
+  int pixmax = header->pixmax;
+  int outmax = pixmax - pixmin;
+  
+  linebuf   = mymalloc(width * sizeof(i_fcolor));  /* checked 31Jul07 TonyC */
+  databuf   = mymalloc(width * 2);  /* checked 31Jul07 TonyC */
+
+  savemask = i_img_getmask(img);
+
+  for(c = 0; c < channels; c++) {
+    i_img_setmask(img, 1<<c);
+    for(y = 0; y < height; y++) {
+      int x;
+      
+      if (ig->readcb(ig, databuf, width*2) != width*2) {
+       i_push_error(0, "SGI image: cannot read image data");
+       i_img_destroy(img);
+       myfree(linebuf);
+       myfree(databuf);
+       return NULL;
+      }
+
+      if (pixmin == 0 && pixmax == 65535) {
+       for(x = 0; x < img->xsize; x++)
+         linebuf[x].channel[c] = (databuf[x*2] * 256 + databuf[x*2+1]) / 65535.0;
+      }
+      else {
+       for(x = 0; x < img->xsize; x++) {
+         int sample = databuf[x*2] * 256 + databuf[x*2+1];
+         if (sample < pixmin)
+           sample = 0;
+         else if (sample > pixmax)
+           sample = outmax;
+         else
+           sample -= pixmin;
+           
+         linebuf[x].channel[c] = (double)sample / outmax;
+       }
+      }
+      
+      i_plinf(img, 0, width, height-1-y, linebuf);
+    }
+  }
+  i_img_setmask(img, savemask);
+
+  myfree(linebuf);
+  myfree(databuf);
+  
+  return img;
+}
+
+static i_img *
+read_rgb_16_rle(i_img *img, io_glue *ig, rgb_header const *header) {
+  i_fcolor *linebuf = NULL;
+  unsigned char *databuf = NULL;
+  unsigned long *start_tab, *length_tab;
+  unsigned long max_length;
+  i_img_dim width = i_img_get_width(img);
+  i_img_dim height = i_img_get_height(img);
+  int channels = i_img_getchannels(img);;
+  i_img_dim y;
+  int c;
+  int pixmin = header->pixmin;
+  int pixmax = header->pixmax;
+  int outmax = pixmax - pixmin;
+
+  if (!read_rle_tables(ig, img,  
+                      &start_tab, &length_tab, &max_length)) {
+    i_img_destroy(img);
+    return NULL;
+  }
+
+  mm_log((1, "maxlen for an rle buffer: %lu\n", max_length));
+
+  if (max_length > (img->xsize * 2 + 1) * 2) {
+    i_push_errorf(0, "SGI image: ridiculous RLE line length %lu", max_length);
+    goto ErrorReturn;
+  }
+
+  linebuf = mymalloc(width*sizeof(i_fcolor)); /* checked 31Jul07 TonyC */
+  databuf = mymalloc(max_length); /* checked 31Jul07 TonyC */
+
+  for(y = 0; y < img->ysize; y++) {
+    for(c = 0; c < channels; c++) {
+      int ci = height * c + y;
+      int datalen = length_tab[ci];
+      unsigned char *inp;
+      i_fcolor *outp;
+      int data_left = datalen;
+      int pixels_left = width;
+      int sample;
+      
+      if (datalen & 1) {
+       i_push_error(0, "SGI image: invalid RLE length value for BPC=2");
+       goto ErrorReturn;
+      }
+      if (ig->seekcb(ig, start_tab[ci], SEEK_SET) != start_tab[ci]) {
+       i_push_error(0, "SGI image: cannot seek to RLE data");
+       goto ErrorReturn;
+      }
+      if (ig->readcb(ig, databuf, datalen) != datalen) {
+       i_push_error(0, "SGI image: cannot read RLE data");
+       goto ErrorReturn;
+      }
+      
+      inp = databuf;
+      outp = linebuf;
+      while (data_left > 0) {
+       int code = inp[0] * 256 + inp[1];
+       int count = code & 0x7f;
+       inp += 2;
+       data_left -= 2;
+
+       if (count == 0)
+         break;
+       if (code & 0x80) {
+         /* literal run */
+         /* sanity checks */
+         if (count > pixels_left) {
+           i_push_error(0, "SGI image: literal run overflows scanline");
+           goto ErrorReturn;
+         }
+         if (count > data_left) {
+           i_push_error(0, "SGI image: literal run consumes more data than available");
+           goto ErrorReturn;
+         }
+         /* copy the run */
+         pixels_left -= count;
+         data_left -= count * 2;
+         if (pixmin == 0 && pixmax == 65535) {
+           while (count-- > 0) {
+             outp->channel[c] = (inp[0] * 256 + inp[1]) / 65535.0;
+             inp += 2;
+             ++outp;
+           }
+         }
+         else {
+           while (count-- > 0) {
+             int sample = inp[0] * 256 + inp[1];
+             if (sample < pixmin)
+               sample = 0;
+             else if (sample > pixmax)
+               sample = outmax;
+             else
+               sample -= pixmin;
+             outp->channel[c] = (double)sample / outmax;
+             ++outp;
+             inp += 2;
+           }
+         }
+       }
+       else {
+         double fsample;
+         /* RLE run */
+         if (count > pixels_left) {
+           i_push_error(0, "SGI image: RLE run overflows scanline");
+           goto ErrorReturn;
+         }
+         if (data_left < 2) {
+           i_push_error(0, "SGI image: RLE run has no data for pixel");
+           goto ErrorReturn;
+         }
+         sample = inp[0] * 256 + inp[1];
+         inp += 2;
+         data_left -= 2;
+         if (pixmin != 0 || pixmax != 65535) {
+           if (sample < pixmin)
+             sample = 0;
+           else if (sample > pixmax)
+             sample = outmax;
+           else
+             sample -= pixmin;
+           fsample = (double)sample / outmax;
+         }
+         else {
+           fsample = (double)sample / 65535.0;
+         }
+         pixels_left -= count;
+         while (count-- > 0) {
+           outp->channel[c] = fsample;
+           ++outp;
+         }
+       }
+      }
+      /* must have a full scanline */
+      if (pixels_left) {
+       i_push_error(0, "SGI image: incomplete RLE scanline");
+       goto ErrorReturn;
+      }
+      /* must have used all of the data */
+      if (data_left) {
+       i_push_errorf(0, "SGI image: unused RLE data");
+       goto ErrorReturn;
+      }
+    }
+    i_plinf(img, 0, width, height-1-y, linebuf);
+  }
+
+  myfree(linebuf);
+  myfree(databuf);
+  myfree(start_tab);
+  myfree(length_tab);
+
+  return img;
+
+ ErrorReturn:
+  if (linebuf)
+    myfree(linebuf);
+  if (databuf)
+    myfree(databuf);
+  myfree(start_tab);
+  myfree(length_tab);
+  i_img_destroy(img);
+  return NULL;
+}
+
+static int
+write_sgi_header(i_img *img, io_glue *ig, int *rle, int *bpc2) {
+  rgb_header header;
+  unsigned char headbuf[512] = { 0 };
+
+  header.imagic = SGI_MAGIC;
+  if (!i_tags_get_int(&img->tags, "sgi_rle", 0, rle))
+    *rle = 0;
+  header.storagetype = *rle ? SGI_STORAGE_RLE : SGI_STORAGE_VERBATIM;
+  header.pixmin = 0;
+  header.colormap = SGI_COLORMAP_NORMAL;
+  *bpc2 = img->bits > 8;
+  if (*bpc2) {
+    header.BPC = 2;
+    header.pixmax = 65535;
+  }
+  else {
+    header.BPC = 1;
+    header.pixmax = 255;
+  }
+  if (img->channels == 1) {
+    header.dimensions = 2;
+  }
+  else {
+    header.dimensions = 3;
+  }
+  header.xsize = img->xsize;
+  header.ysize = img->ysize;
+  header.zsize = img->channels;
+  memset(header.name, 0, sizeof(header.name));
+  i_tags_get_string(&img->tags, "i_comment",  0, 
+                   header.name, sizeof(header.name));
+
+  rgb_header_pack(&header, headbuf);
+
+  if (i_io_write(ig, headbuf, sizeof(headbuf)) != sizeof(headbuf)) {
+    i_push_error(0, "SGI image: cannot write header");
+    return 0;
+  }
+
+  return 1;
+}
+
+static int
+write_sgi_8_verb(i_img *img, io_glue *ig) {
+  i_sample_t *linebuf;
+  i_img_dim width = img->xsize;
+  int c;
+  i_img_dim y;
+
+  linebuf = mymalloc(width);  /* checked 31Jul07 TonyC */
+  for (c = 0; c < img->channels; ++c) {
+    for (y = img->ysize - 1; y >= 0; --y) {
+      i_gsamp(img, 0, width, y, linebuf, &c, 1);
+      if (ig->writecb(ig, linebuf, width) != width) {
+       i_push_error(errno, "SGI image: error writing image data");
+       myfree(linebuf);
+       return 0;
+      }
+    }
+  }
+  myfree(linebuf);
+
+  return 1;
+}
+
+static int
+write_sgi_8_rle(i_img *img, io_glue *ig) {
+  i_sample_t *linebuf;
+  unsigned char *comp_buf;
+  i_img_dim width = img->xsize;
+  int c;
+  i_img_dim y;
+  unsigned char *offsets;
+  unsigned char *lengths;
+  int offset_pos = 0;
+  size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
+  unsigned long start_offset = 512 + offsets_size;
+  unsigned long current_offset = start_offset;
+  int in_left;
+  unsigned char *outp;
+  i_sample_t *inp;
+  size_t comp_size;
+
+  if (offsets_size / 2 / 4 / img->channels != img->ysize) {
+    i_push_error(0, "SGI image: integer overflow calculating allocation size");
+    return 0;
+  }
+
+  linebuf = mymalloc(width);  /* checked 31Jul07 TonyC */
+  comp_buf = mymalloc((width + 1) * 2);  /* checked 31Jul07 TonyC */
+  offsets = mymalloc(offsets_size);
+  memset(offsets, 0, offsets_size);
+  if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
+    i_push_error(errno, "SGI image: error writing offsets/lengths");
+    goto Error;
+  }
+  lengths = offsets + img->ysize * img->channels * 4;
+  for (c = 0; c < img->channels; ++c) {
+    for (y = img->ysize - 1; y >= 0; --y) {
+      i_gsamp(img, 0, width, y, linebuf, &c, 1);
+      in_left = width;
+      outp = comp_buf;
+      inp = linebuf;
+      while (in_left) {
+       unsigned char *run_start = inp;
+
+       /* first try for an RLE run */
+       int run_length = 1;
+       while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
+         ++run_length;
+         ++inp;
+       }
+       if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
+         ++run_length;
+         ++inp;
+       }
+       if (run_length > 2) {
+         *outp++ = run_length;
+         *outp++ = inp[0];
+         inp++;
+         in_left -= run_length;
+       }
+       else {
+         inp = run_start;
+
+         /* scan for a literal run */
+         run_length = 1;
+         run_start = inp;
+         while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
+           ++run_length;
+           ++inp;
+         }
+         ++inp;
+         
+         /* fill out the run if 2 or less samples left and there's space */
+         if (in_left - run_length <= 2 
+             && run_length + in_left - run_length <= 127) {
+           run_length += in_left;
+           in_left = 0;
+         }
+         in_left -= run_length;
+         *outp++ = run_length | 0x80;
+         while (run_length--) {
+           *outp++ = *run_start++;
+         }
+       }
+      }
+      *outp++ = 0;
+      comp_size = outp - comp_buf;
+      store_32(offsets + offset_pos, current_offset);
+      store_32(lengths + offset_pos, comp_size);
+      offset_pos += 4;
+      current_offset += comp_size;
+      if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
+       i_push_error(errno, "SGI image: error writing RLE data");
+       goto Error;
+      }
+    }
+  }
+
+  /* seek back to store the offsets and lengths */
+  if (i_io_seek(ig, 512, SEEK_SET) != 512) {
+    i_push_error(errno, "SGI image: cannot seek to RLE table");
+    goto Error;
+  }
+
+  if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
+    i_push_error(errno, "SGI image: cannot write final RLE table");
+    goto Error;
+  }
+
+  myfree(offsets);
+  myfree(comp_buf);
+  myfree(linebuf);
+
+  return 1;
+
+ Error:
+  myfree(offsets);
+  myfree(comp_buf);
+  myfree(linebuf);
+  return 0;
+}
+
+static int
+write_sgi_16_verb(i_img *img, io_glue *ig) {
+  i_fsample_t *linebuf;
+  unsigned char *encbuf;
+  unsigned char *outp;
+  i_img_dim width = img->xsize;
+  int c;
+  i_img_dim x;
+  i_img_dim y;
+
+  linebuf = mymalloc(width * sizeof(i_fsample_t));  /* checked 31Jul07 TonyC */
+  encbuf = mymalloc(width * 2);  /* checked 31Jul07 TonyC */
+  for (c = 0; c < img->channels; ++c) {
+    for (y = img->ysize - 1; y >= 0; --y) {
+      i_gsampf(img, 0, width, y, linebuf, &c, 1);
+      for (x = 0, outp = encbuf; x < width; ++x, outp+=2) {
+       unsigned short samp16 = SampleFTo16(linebuf[x]);
+       store_16(outp, samp16);
+      }
+      if (ig->writecb(ig, encbuf, width * 2) != width * 2) {
+       i_push_error(errno, "SGI image: error writing image data");
+       myfree(linebuf);
+       myfree(encbuf);
+       return 0;
+      }
+    }
+  }
+  myfree(linebuf);
+  myfree(encbuf);
+
+  return 1;
+}
+
+static int
+write_sgi_16_rle(i_img *img, io_glue *ig) {
+  i_fsample_t *sampbuf;
+  unsigned short *linebuf;
+  unsigned char *comp_buf;
+  i_img_dim width = img->xsize;
+  int c;
+  i_img_dim y;
+  unsigned char *offsets;
+  unsigned char *lengths;
+  int offset_pos = 0;
+  size_t offsets_size = (size_t)4 * img->ysize * img->channels * 2;
+  unsigned long start_offset = 512 + offsets_size;
+  unsigned long current_offset = start_offset;
+  int in_left;
+  unsigned char *outp;
+  unsigned short *inp;
+  size_t comp_size;
+  i_img_dim x;
+
+  if (offsets_size / 4 / 2 / img->channels != img->ysize) {
+    i_push_error(0, "SGI image: integer overflow calculating allocation size");
+    return 0;
+  }
+
+  sampbuf = mymalloc(width * sizeof(i_fsample_t));  /* checked 31Jul07 TonyC */
+  linebuf = mymalloc(width * sizeof(unsigned short));  /* checked 31Jul07 TonyC */
+  comp_buf = mymalloc((width + 1) * 2 * 2);  /* checked 31Jul07 TonyC */
+  offsets = mymalloc(offsets_size);
+  memset(offsets, 0, offsets_size);
+  if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
+    i_push_error(errno, "SGI image: error writing offsets/lengths");
+    goto Error;
+  }
+  lengths = offsets + img->ysize * img->channels * 4;
+  for (c = 0; c < img->channels; ++c) {
+    for (y = img->ysize - 1; y >= 0; --y) {
+      i_gsampf(img, 0, width, y, sampbuf, &c, 1);
+      for (x = 0; x < width; ++x)
+       linebuf[x] = (unsigned short)(SampleFTo16(sampbuf[x]));
+      in_left = width;
+      outp = comp_buf;
+      inp = linebuf;
+      while (in_left) {
+       unsigned short *run_start = inp;
+
+       /* first try for an RLE run */
+       int run_length = 1;
+       while (in_left - run_length >= 2 && inp[0] == inp[1] && run_length < 127) {
+         ++run_length;
+         ++inp;
+       }
+       if (in_left - run_length == 1 && inp[0] == inp[1] && run_length < 127) {
+         ++run_length;
+         ++inp;
+       }
+       if (run_length > 2) {
+         store_16(outp, run_length);
+         store_16(outp+2, inp[0]);
+         outp += 4;
+         inp++;
+         in_left -= run_length;
+       }
+       else {
+         inp = run_start;
+
+         /* scan for a literal run */
+         run_length = 1;
+         run_start = inp;
+         while (in_left - run_length > 1 && (inp[0] != inp[1] || inp[1] != inp[2]) && run_length < 127) {
+           ++run_length;
+           ++inp;
+         }
+         ++inp;
+         
+         /* fill out the run if 2 or less samples left and there's space */
+         if (in_left - run_length <= 2 
+             && run_length + in_left - run_length <= 127) {
+           run_length += in_left;
+           in_left = 0;
+         }
+         in_left -= run_length;
+         store_16(outp, run_length | 0x80);
+         outp += 2;
+         while (run_length--) {
+           store_16(outp, *run_start++);
+           outp += 2;
+         }
+       }
+      }
+      store_16(outp, 0);
+      outp += 2;
+      comp_size = outp - comp_buf;
+      store_32(offsets + offset_pos, current_offset);
+      store_32(lengths + offset_pos, comp_size);
+      offset_pos += 4;
+      current_offset += comp_size;
+      if (ig->writecb(ig, comp_buf, comp_size) != comp_size) {
+       i_push_error(errno, "SGI image: error writing RLE data");
+       goto Error;
+      }
+    }
+  }
+
+  /* seek back to store the offsets and lengths */
+  if (i_io_seek(ig, 512, SEEK_SET) != 512) {
+    i_push_error(errno, "SGI image: cannot seek to RLE table");
+    goto Error;
+  }
+
+  if (i_io_write(ig, offsets, offsets_size) != offsets_size) {
+    i_push_error(errno, "SGI image: cannot write final RLE table");
+    goto Error;
+  }
+
+  myfree(offsets);
+  myfree(comp_buf);
+  myfree(linebuf);
+  myfree(sampbuf);
+
+  return 1;
+
+ Error:
+  myfree(offsets);
+  myfree(comp_buf);
+  myfree(linebuf);
+  myfree(sampbuf);
+
+  return 0;
+}
diff --git a/SGI/imsgi.h b/SGI/imsgi.h
new file mode 100644 (file)
index 0000000..e696f95
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef IMAGER_IMICON_H
+#define IMAGER_IMICON_H
+
+#include "imext.h"
+
+extern i_img *
+i_readsgi_wiol(io_glue *ig, int partial);
+
+extern int
+i_writesgi_wiol(i_io_glue_t *ig, i_img *im);
+
+#endif
diff --git a/SGI/t/00load.t b/SGI/t/00load.t
new file mode 100644 (file)
index 0000000..51f57ab
--- /dev/null
@@ -0,0 +1,5 @@
+#!perl -w
+use strict;
+use Test::More tests => 1;
+
+use_ok('Imager::File::SGI');
diff --git a/SGI/t/10read.t b/SGI/t/10read.t
new file mode 100644 (file)
index 0000000..875bcff
--- /dev/null
@@ -0,0 +1,318 @@
+#!perl -w
+use strict;
+use Imager;
+use Imager::Test qw(is_image is_color3);
+use Test::More tests => 103;
+
+-d 'testout' or mkdir 'testout';
+
+Imager::init_log('testout/10read.log', 2);
+
+{
+  my $im_verb = Imager->new;
+  ok($im_verb->read(file => 'testimg/verb.rgb'), "read verbatim")
+    or print "# ", $im_verb->errstr, "\n";
+  is($im_verb->getchannels, 3, "check channels");
+  is($im_verb->getwidth, 20, "check width");
+  is($im_verb->getheight, 20, "check height");
+  is_color3($im_verb->getpixel(x => 0, 'y' => 0), 255, 0, 0, "check 0,0");
+  is_color3($im_verb->getpixel(x => 1, 'y' => 2), 255, 255, 0, "check 0,2");
+  is_color3($im_verb->getpixel(x => 2, 'y' => 4), 0, 255, 255, "check 2,5");
+  is($im_verb->tags(name => 'i_format'), 'sgi', "check i_format tag");
+  is($im_verb->tags(name => 'sgi_rle'), 0, "check sgi_rgb");
+  is($im_verb->tags(name => 'sgi_pixmin'), 0, "check pixmin");
+  is($im_verb->tags(name => 'sgi_pixmax'), 255, "check pixmax");
+  is($im_verb->tags(name => 'sgi_bpc'), 1, "check bpc");
+  is($im_verb->tags(name => 'i_comment'), 'test image', 
+     "check name string");
+
+  my $im_rle = Imager->new;
+  ok($im_rle->read(file => 'testimg/rle.rgb'), "read rle")
+    or print "# ", $im_rle->errstr, "\n";
+  is($im_rle->tags(name => 'sgi_rle'), 1, "check sgi_rgb");
+
+  my $im_rleagr = Imager->new;
+  ok($im_rleagr->read(file => 'testimg/rleagr.rgb'), "read rleagr")
+    or print "# ", $im_rleagr->errstr, "\n";
+
+  my $im6 = Imager->new;
+  ok($im6->read(file => 'testimg/verb6.rgb'), "read verbatim 6-bit")
+    or print "# ", $im6->errstr, "\n";
+  is($im6->tags(name => 'sgi_pixmax'), 63, "check pixmax");
+
+  is_image($im_verb, $im_rle, "compare verbatim to rle");
+  is_image($im_verb, $im_rleagr, "compare verbatim to rleagr");
+  is_image($im_verb, $im6, "compare verbatim to verb 6-bit");
+
+  my $im_verb12 = Imager->new;
+  ok($im_verb12->read(file => 'testimg/verb12.rgb'), "read verbatim 12")
+    or print "# ", $im_verb12->errstr, "\n";
+  is($im_verb12->bits, 16, "check bits on verb12");
+  is($im_verb12->tags(name => 'sgi_pixmax'), 4095, "check pixmax");
+
+  my $im_verb16 = Imager->new;
+  ok($im_verb16->read(file => 'testimg/verb16.rgb'), "read verbatim 16")
+    or print "# ", $im_verb16->errstr, "\n";
+  is($im_verb16->bits, 16, "check bits on verb16");
+  is($im_verb16->tags(name => 'sgi_pixmax'), 65535, "check pixmax");
+  
+  is_image($im_verb, $im_verb12, "compare verbatim to verb12");
+  is_image($im_verb, $im_verb16, "compare verbatim to verb16");
+
+  my $im_rle6 = Imager->new;
+  ok($im_rle6->read(file => 'testimg/rle6.rgb'), "read rle 6 bit");
+  is($im_rle6->tags(name => 'sgi_pixmax'), 63, 'check pixmax');
+  is_image($im_verb, $im_rle6, 'compare verbatim to rle6');
+  
+  my $im_rle12 = Imager->new;
+  ok($im_rle12->read(file => 'testimg/rle12.rgb'), 'read rle 12 bit')
+    or print "# ", $im_rle12->errstr, "\n";
+  is($im_rle12->tags(name => 'sgi_pixmax'), 4095, 'check pixmax');
+  is_image($im_verb, $im_rle12, 'compare verbatim to rle12');
+
+  my $im_rle16 = Imager->new;
+  ok($im_rle16->read(file => 'testimg/rle16.rgb'), 'read rle 16 bit')
+    or print "# ", $im_rle16->errstr, "\n";
+  is($im_rle16->tags(name => 'sgi_pixmax'), 65535, 'check pixmax');
+  is($im_rle16->tags(name => 'sgi_bpc'), 2, "check bpc");
+  is_image($im_verb, $im_rle16, 'compare verbatim to rle16');
+}
+
+{
+  # short read tests, each is source file, limit, match, description
+  my @tests =
+    (
+     [ 
+      'verb.rgb', 100, 
+      'SGI image: could not read header', 'header',
+     ],
+     [ 
+      'verb.rgb', 512, 
+       'SGI image: cannot read image data', 
+       'verbatim image data' 
+     ],
+     [
+      'rle.rgb', 512,
+      'SGI image: short read reading RLE start table',
+      'rle start table'
+     ],
+     [
+      'rle.rgb', 752,
+      'SGI image: short read reading RLE length table',
+      'rle length table'
+     ],
+     [
+      'rle.rgb', 0x510,
+      "SGI image: cannot read RLE data",
+      'read rle data'
+     ],
+     [
+      'rle.rgb', 0x50E,
+      "SGI image: cannot seek to RLE data",
+      'seek rle data'
+     ],
+     [
+      'verb16.rgb', 512,
+      'SGI image: cannot read image data',
+      'read image data (16-bit)'
+     ],
+     [
+      'rle16.rgb', 512,
+      'SGI image: short read reading RLE start table',
+      'rle start table (16-bit)',
+     ],
+     [
+      'rle16.rgb', 0x42f,
+      'SGI image: cannot seek to RLE data',
+      'seek RLE data (16-bit)'
+     ],
+     [
+      'rle16.rgb', 0x64A,
+      'SGI image: cannot read RLE data',
+      'read rle image data (16-bit)'
+     ],
+    );
+  for my $test (@tests) {
+    my ($src, $size, $match, $desc) = @$test;
+    open SRC, "< testimg/$src"
+      or die "Cannot open testimg/$src: $!";
+    binmode SRC;
+    my $data;
+    read(SRC, $data, $size) == $size
+      or die "Could not read $size bytes from $src";
+    close SRC;
+    my $im = Imager->new;
+    ok(!$im->read(data => $data, type => 'sgi'),
+       "read: $desc");
+    is($im->errstr, $match, "error match: $desc");
+  }
+}
+
+{
+  # each entry is: source file, patches, expected error, description
+  my @tests =
+    (
+     [
+      'verb.rgb',
+      { 0 => '00 00' },
+      'SGI image: invalid magic number',
+      'bad magic',
+     ],
+     [
+      'verb.rgb',
+      { 104 => '00 00 00 01' },
+      'SGI image: invalid value for colormap (1)',
+      'invalid colormap field',
+     ],
+     [
+      'verb.rgb',
+      { 3 => '03' },
+      'SGI image: invalid value for BPC (3)',
+      'invalid bpc field',
+     ],
+     [
+      'verb.rgb',
+      { 2 => '03' },
+      'SGI image: invalid storage type field',
+      'invalid storage type field',
+     ],
+     [
+      'verb.rgb',
+      { 4 => '00 04' },
+      'SGI image: invalid dimension field',
+      'invalid dimension field',
+     ],
+     [
+      'rle.rgb',
+      { 0x2f0 => '00 00 00 2b' },
+      'SGI image: ridiculous RLE line length 43',
+      'invalid rle length',
+     ],
+     [
+      'rle.rgb',
+      { 0x3E0 => '95' },
+      'SGI image: literal run overflows scanline',
+      'literal run overflow scanline',
+     ],
+     [
+      'rle.rgb',
+      { 0x3E0 => '87' },
+      'SGI image: literal run consumes more data than available',
+      'literal run consuming too much data',
+     ],
+     [
+      'rle.rgb',
+      { 0x3E0 => '15' },
+      'SGI image: RLE run overflows scanline',
+      'RLE run overflows scanline',
+     ],
+     [
+      'rle.rgb',
+      { 0x3E0 => '81 FF 12 00 01' },
+      'SGI image: RLE run has no data for pixel',
+      'RLE run has no data for pixel',
+     ],
+     [
+      'rle.rgb',
+      { 0x3E0 => '81 FF 12 00' },
+      'SGI image: incomplete RLE scanline',
+      'incomplete RLE scanline',
+     ],
+     [
+      'rle.rgb',
+      { 0x2F0 => '00 00 00 06' },
+      'SGI image: unused RLE data',
+      'unused RLE data',
+     ],
+     [
+      'verb.rgb',
+      { 0x0c => '00 00 00 FF 00 00 00 00' },
+      'SGI image: invalid pixmin >= pixmax',
+      'bad pixmin/pixmax',
+     ],
+     [
+      'rle16.rgb',
+      { 0x2f0 => '00 00 00 0B' },
+      'SGI image: invalid RLE length value for BPC=2',
+      'bad RLE table (length) (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x2f0 => '00 00 00 53' },
+      'SGI image: ridiculous RLE line length 83',
+      'way too big RLE line length (16-bit)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x426 => '00 95' },
+      'SGI image: literal run overflows scanline',
+      'literal overflow scanline (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x426 => '00 93' },
+      'SGI image: literal run consumes more data than available',
+      'literal overflow data (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x3EA => '00 15' },
+      'SGI image: RLE run overflows scanline',
+      'rle overflow scanline (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x3EA => '00 15' },
+      'SGI image: RLE run overflows scanline',
+      'rle overflow scanline (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x3EA => '00 83 ff ff ff ff ff ff 00 01' },
+      'SGI image: RLE run has no data for pixel',
+      'rle code no argument (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x3EA => '00 14 ff ff 00 00' },
+      'SGI image: unused RLE data',
+      'unused RLE data (bpc=2)'
+     ],
+     [
+      'rle16.rgb',
+      { 0x3EA => '00 12 ff ff' },
+      'SGI image: incomplete RLE scanline',
+      'incomplete rle scanline (bpc=2)'
+     ],
+    );
+
+  # invalid file tests - take our original files and patch them a
+  # little to make them invalid
+    my $test_index = 0;
+  for my $test (@tests) {
+    my ($filename, $patches, $error, $desc) = @$test;
+
+    my $data = load_patched_file("testimg/$filename", $patches);
+    my $im = Imager->new;
+    ok(!$im->read(data => $data, type=>'sgi'),
+       "$test_index - $desc:should fail to read");
+    is($im->errstr, $error, "$test_index - $desc:check message");
+    ++$test_index;
+  }
+}
+
+sub load_patched_file {
+  my ($filename, $patches) = @_;
+
+  open IMDATA, "< $filename"
+    or die "Cannot open $filename: $!";
+  binmode IMDATA;
+  my $data = do { local $/; <IMDATA> };
+  for my $offset (keys %$patches) {
+    (my $hdata = $patches->{$offset}) =~ tr/ //d;
+    my $pdata = pack("H*", $hdata);
+    substr($data, $offset, length $pdata) = $pdata;
+  }
+
+  return $data;
+}
diff --git a/SGI/t/20write.t b/SGI/t/20write.t
new file mode 100644 (file)
index 0000000..5371f81
--- /dev/null
@@ -0,0 +1,197 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 51;
+use Imager::Test qw(test_image test_image_16 is_image);
+use Fcntl ':seek';
+
+-d 'testout' or mkdir 'testout';
+
+Imager::init_log('testout/20write.log', 2);
+
+{
+  my $im = test_image();
+  ok($im->write(file => 'testout/20verb.rgb'), "write 8-bit verbatim")
+    or print "# ", $im->errstr, "\n";
+  my $im2 = Imager->new;
+  ok($im2->read(file => 'testout/20verb.rgb'), "read it back")
+    or print "# ", $im2->errstr, "\n";
+  is_image($im, $im2, "compare");
+  is($im2->tags(name => 'sgi_rle'), 0, "check not rle");
+  is($im2->tags(name => 'sgi_bpc'), 1, "check bpc");
+  is($im2->tags(name => 'i_comment'), undef, "no namestr");
+  
+  ok($im->write(file => 'testout/20rle.rgb', 
+               sgi_rle => 1, 
+               i_comment => "test"), "write 8-bit rle")
+    or print "# ", $im->errstr, "\n";
+  my $im3 = Imager->new;
+  ok($im3->read(file => 'testout/20rle.rgb'), "read it back")
+    or print "# ", $im3->errstr, "\n";
+  is_image($im, $im3, "compare");
+  is($im3->tags(name => 'sgi_rle'), 1, "check not rle");
+  is($im3->tags(name => 'sgi_bpc'), 1, "check bpc");
+  is($im3->tags(name => 'i_comment'), 'test', "check i_comment set");
+}
+
+{
+  my $im = test_image_16();
+  ok($im->write(file => 'testout/20verb16.rgb'), "write 16-bit verbatim")
+    or print "# ", $im->errstr, "\n";
+  my $im2 = Imager->new;
+  ok($im2->read(file => 'testout/20verb16.rgb'), "read it back")
+    or print "# ", $im2->errstr, "\n";
+  is_image($im, $im2, "compare");
+  is($im2->tags(name => 'sgi_rle'), 0, "check not rle");
+  is($im2->tags(name => 'sgi_bpc'), 2, "check bpc");
+  is($im2->tags(name => 'i_comment'), undef, "no namestr");
+  
+  ok($im->write(file => 'testout/20rle16.rgb', 
+               sgi_rle => 1, 
+               i_comment => "test"), "write 16-bit rle")
+    or print "# ", $im->errstr, "\n";
+  my $im3 = Imager->new;
+  ok($im3->read(file => 'testout/20rle16.rgb'), "read it back")
+    or print "# ", $im3->errstr, "\n";
+  is_image($im, $im3, "compare");
+  is($im3->tags(name => 'sgi_rle'), 1, "check not rle");
+  is($im3->tags(name => 'sgi_bpc'), 2, "check bpc");
+  is($im3->tags(name => 'i_comment'), 'test', "check i_comment set");
+}
+
+{
+  # grey scale check
+  my $im = test_image()->convert(preset=>'grey');
+  ok($im->write(file => 'testout/20vgray8.bw'), "write 8-bit verbatim grey")
+    or print "# ", $im->errstr, "\n";
+  my $im2 = Imager->new;
+  ok($im2->read(file => 'testout/20vgray8.bw'), "read it back")
+    or print "# ", $im2->errstr, "\n";
+  is_image($im, $im2, "compare");
+  is($im2->tags(name => 'i_format'), 'sgi', "check we saved as SGI");
+  is($im2->tags(name => 'sgi_rle'), 0, "check not rle");
+  is($im2->tags(name => 'sgi_bpc'), 1, "check bpc");
+  is($im2->tags(name => 'i_comment'), undef, "no namestr");
+}
+
+{
+  # write failure tests
+  my $rgb8 = test_image();
+  my $rgb16 = test_image_16();
+  my $rgb8rle = $rgb8->copy;
+  $rgb8rle->settag(name => 'sgi_rle', value => 1);
+  my $grey8 = $rgb8->convert(preset => 'grey');
+  my $grey16 = $rgb16->convert(preset => 'grey');
+  my $grey16rle = $grey16->copy;
+  $grey16rle->settag(name => 'sgi_rle', value => 1);
+
+  my @tests =
+    (
+     # each entry is: image, limit, expected msg, description
+     [ 
+      $rgb8, 500, 
+      'SGI image: cannot write header', 
+      'writing header' 
+     ],
+     [ 
+      $rgb8, 1024, 
+      'SGI image: error writing image data', 
+      '8-bit image data' 
+     ],
+     [
+      $grey8, 513,
+      'SGI image: error writing image data',
+      '8-bit image data (grey)'
+     ],
+     [
+      $rgb8rle, 513,
+      'SGI image: error writing offsets/lengths',
+      'rle tables, 8 bit',
+     ],
+     [
+      $rgb8rle, 4112,
+      'SGI image: error writing RLE data',
+      '8-bit rle data',
+     ],
+     [
+      $rgb8rle, 14707,
+      'SGI image: cannot write final RLE table',
+      '8-bit rewrite RLE table',
+     ],
+     [
+      $rgb16, 513,
+      'SGI image: error writing image data',
+      '16-bit image data',
+     ],
+     [
+      $grey16rle, 513,
+      'SGI image: error writing offsets/lengths',
+      'rle tables, 16 bit',
+     ],
+     [
+      $grey16rle, 1713,
+      'SGI image: error writing RLE data',
+      '16-bit rle data',
+     ],
+     [
+      $grey16rle, 10871,
+      'SGI image: cannot write final RLE table',
+      '16-bit rewrite RLE table',
+     ],
+    );
+  for my $test (@tests) {
+    my ($im, $limit, $expected_msg, $desc) = @$test;
+    my ($writecb, $seekcb) = limited_write($limit);
+    ok(!$im->write(type => 'sgi', writecb => $writecb,
+                  seekcb => $seekcb, maxbuffer => 1),
+       "write should fail - $desc");
+    is($im->errstr, "$expected_msg: limit reached", "check error - $desc");
+  }
+}
+
+sub limited_write {
+  my ($limit) = @_;
+
+  my $pos = 0;
+  my $written = 0;
+  return
+    (
+     # write callback
+     sub {
+       my ($data) = @_;
+       # limit total written so we can fail the offset table write for RLE
+       $written += length $data;
+       if ($written <= $limit) {
+        $pos += length $data;
+         print "# write of ", length $data, " bytes successful (", 
+          $limit - $written, " left)\n";
+         return 1;
+       }
+       else {
+         print "# write of ", length $data, " bytes failed\n";
+         Imager::i_push_error(0, "limit reached");
+         return;
+       }
+     },
+     # seek cb
+     sub {
+       my ($position, $whence) = @_;
+
+       if ($whence == SEEK_SET) {
+        $pos = $position;
+        print "# seek to $pos\n";
+       }
+       elsif ($whence == SEEK_END) {
+        die "SEEK_END not supported\n";
+       }
+       elsif ($whence == SEEK_CUR) {
+        die "SEEK_CUR not supported\n";
+       }
+       else {
+        die "Invalid seek whence $whence";
+       }
+
+       $pos;
+     }
+    )
+}
diff --git a/SGI/testimg/rle.rgb b/SGI/testimg/rle.rgb
new file mode 100644 (file)
index 0000000..59742ed
Binary files /dev/null and b/SGI/testimg/rle.rgb differ
diff --git a/SGI/testimg/rle12.rgb b/SGI/testimg/rle12.rgb
new file mode 100644 (file)
index 0000000..c28ab34
Binary files /dev/null and b/SGI/testimg/rle12.rgb differ
diff --git a/SGI/testimg/rle16.rgb b/SGI/testimg/rle16.rgb
new file mode 100644 (file)
index 0000000..94ff618
Binary files /dev/null and b/SGI/testimg/rle16.rgb differ
diff --git a/SGI/testimg/rle6.rgb b/SGI/testimg/rle6.rgb
new file mode 100644 (file)
index 0000000..6aefaab
Binary files /dev/null and b/SGI/testimg/rle6.rgb differ
diff --git a/SGI/testimg/rleagr.rgb b/SGI/testimg/rleagr.rgb
new file mode 100644 (file)
index 0000000..02ce22a
Binary files /dev/null and b/SGI/testimg/rleagr.rgb differ
diff --git a/SGI/testimg/verb.rgb b/SGI/testimg/verb.rgb
new file mode 100644 (file)
index 0000000..9808eb3
Binary files /dev/null and b/SGI/testimg/verb.rgb differ
diff --git a/SGI/testimg/verb12.rgb b/SGI/testimg/verb12.rgb
new file mode 100644 (file)
index 0000000..716ae31
Binary files /dev/null and b/SGI/testimg/verb12.rgb differ
diff --git a/SGI/testimg/verb16.rgb b/SGI/testimg/verb16.rgb
new file mode 100644 (file)
index 0000000..e6119b8
Binary files /dev/null and b/SGI/testimg/verb16.rgb differ
diff --git a/SGI/testimg/verb6.rgb b/SGI/testimg/verb6.rgb
new file mode 100644 (file)
index 0000000..bac9d71
Binary files /dev/null and b/SGI/testimg/verb6.rgb differ
index 60144ee4aee0b28714086b239f928128fcf1f562..db8ac1d2fa506313cf8eca7585227fd35cb9f39d 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -21,7 +21,7 @@ converting from RGBA to greyscale and back.
 
 
 /*
 
 
 /*
-=item i_convert(im, src, coeff, outchan, inchan)
+=item i_convert(src, coeff, outchan, inchan)
 
 Converts the image src into another image.
 
 
 Converts the image src into another image.
 
@@ -40,14 +40,15 @@ Now handles images with more than 8-bits/sample.
 =cut
 */
 
 =cut
 */
 
-int
-i_convert(i_img *im, i_img *src, const float *coeff, int outchan, int inchan) {
+i_img *
+i_convert(i_img *src, const float *coeff, int outchan, int inchan) {
   int x, y;
   int i, j;
   int ilimit;
   double work[MAXCHANNELS];
   int x, y;
   int i, j;
   int ilimit;
   double work[MAXCHANNELS];
+  i_img *im = NULL;
 
 
-  mm_log((1,"i_convert(im %p, src, %p, coeff %p,outchan %d, inchan %d)\n",im,src, coeff,outchan, inchan));
+  mm_log((1,"i_convert(src %p, coeff %p,outchan %d, inchan %d)\n",im,src, coeff,outchan, inchan));
  
   i_clear_error();
 
  
   i_clear_error();
 
@@ -59,14 +60,9 @@ i_convert(i_img *im, i_img *src, const float *coeff, int outchan, int inchan) {
     return 0;
   }
 
     return 0;
   }
 
-  if (im->type == i_direct_type || src->type == i_direct_type) {
-    /* first check the output image */
-    if (im->channels != outchan || im->xsize != src->xsize 
-        || im->ysize != src->ysize) {
-      i_img_exorcise(im);
-      i_img_empty_ch(im, src->xsize, src->ysize, outchan);
-    }
-    if (im->bits == i_8_bits && src->bits == i_8_bits) {
+  if (src->type == i_direct_type) {
+    im = i_sametype_chans(src, src->xsize, src->ysize, outchan);
+    if (src->bits == i_8_bits) {
       i_color *vals;
 
       /* we can always allocate a single scanline of i_color */
       i_color *vals;
 
       /* we can always allocate a single scanline of i_color */
@@ -135,13 +131,9 @@ i_convert(i_img *im, i_img *src, const float *coeff, int outchan, int inchan) {
     i_color *colors;
     i_palidx *vals;
 
     i_color *colors;
     i_palidx *vals;
 
-    if (im->channels != outchan || im->xsize != src->xsize 
-        || im->ysize != src->ysize
-        || i_maxcolors(im) < i_colorcount(src)) {
-      i_img_exorcise(im);
-      i_img_pal_new_low(im, src->xsize, src->ysize, outchan, 
-                        i_maxcolors(src));
-    }
+    im = i_img_pal_new(src->xsize, src->ysize, outchan, 
+                      i_maxcolors(src));
+
     /* just translate the color table */
     count = i_colorcount(src);
     outcount = i_colorcount(im);
     /* just translate the color table */
     count = i_colorcount(src);
     outcount = i_colorcount(im);
@@ -187,7 +179,7 @@ i_convert(i_img *im, i_img *src, const float *coeff, int outchan, int inchan) {
     myfree(colors);
   }
 
     myfree(colors);
   }
 
-  return 1;
+  return im;
 }
 
 /*
 }
 
 /*
diff --git a/feat.h b/feat.h
index 1a7289d35bf06fabad4b310c86221302c237cd78..25fac9e320df011f240fa599df556ef51da745eb 100644 (file)
--- a/feat.h
+++ b/feat.h
@@ -29,7 +29,6 @@ static char *i_format_list[]={
   "pnm",
   "bmp",
   "tga",
   "pnm",
   "bmp",
   "tga",
-  "rgb",
   "ifs",
   NULL};
 
   "ifs",
   NULL};
 
diff --git a/image.c b/image.c
index 03a06f44f7633889a8cf88c186b292c8d699ad52..4f21c161cee7a54f3d677e14ef15ccf274795822 100644 (file)
--- a/image.c
+++ b/image.c
@@ -481,6 +481,9 @@ i_img_info(i_img *im,int *info) {
 /*
 =item i_img_setmask(im, ch_mask)
 
 /*
 =item i_img_setmask(im, ch_mask)
 
+=synopsis // only channel 0 writeable 
+=synopsis i_img_setmask(img, 0x01);
+
 Set the image channel mask for I<im> to I<ch_mask>.
 
 =cut
 Set the image channel mask for I<im> to I<ch_mask>.
 
 =cut
@@ -492,6 +495,8 @@ i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; }
 /*
 =item i_img_getmask(im)
 
 /*
 =item i_img_getmask(im)
 
+=synopsis mask = i_img_getmask(img);
+
 Get the image channel mask for I<im>.
 
 =cut
 Get the image channel mask for I<im>.
 
 =cut
@@ -502,6 +507,8 @@ i_img_getmask(i_img *im) { return im->ch_mask; }
 /*
 =item i_img_getchannels(im)
 
 /*
 =item i_img_getchannels(im)
 
+=synopsis channels = i_img_getchannels(img);
+
 Get the number of channels in I<im>.
 
 =cut
 Get the number of channels in I<im>.
 
 =cut
@@ -509,7 +516,33 @@ Get the number of channels in I<im>.
 int
 i_img_getchannels(i_img *im) { return im->channels; }
 
 int
 i_img_getchannels(i_img *im) { return im->channels; }
 
+/*
+=item i_img_get_width(im)
+
+=synopsis width = i_img_get_width(im);
 
 
+Returns the width in pixels of the image.
+
+=cut
+*/
+i_img_dim
+i_img_get_width(i_img *im) {
+  return im->xsize;
+}
+
+/*
+=item i_img_get_height(im)
+
+=synopsis height = i_img_get_height(im);
+
+Returns the height in pixels of the image.
+
+=cut
+*/
+i_img_dim
+i_img_get_height(i_img *im) {
+  return im->ysize;
+}
 
 /*
 =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
 
 /*
 =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
@@ -2079,10 +2112,10 @@ i_test_format_probe(io_glue *data, int length) {
        on similar files 
        values are: 2 byte magic, rle flags (0 or 1), bytes/sample (1 or 2)
     */
        on similar files 
        values are: 2 byte magic, rle flags (0 or 1), bytes/sample (1 or 2)
     */
-    FORMAT_ENTRY("\x01\xDA\x00\x01", "rgb"),
-    FORMAT_ENTRY("\x01\xDA\x00\x02", "rgb"),
-    FORMAT_ENTRY("\x01\xDA\x01\x01", "rgb"),
-    FORMAT_ENTRY("\x01\xDA\x01\x02", "rgb"),
+    FORMAT_ENTRY("\x01\xDA\x00\x01", "sgi"),
+    FORMAT_ENTRY("\x01\xDA\x00\x02", "sgi"),
+    FORMAT_ENTRY("\x01\xDA\x01\x01", "sgi"),
+    FORMAT_ENTRY("\x01\xDA\x01\x02", "sgi"),
     
     FORMAT_ENTRY2("FORM    ILBM", "ilbm", "xxxx    xxxx"),
 
     
     FORMAT_ENTRY2("FORM    ILBM", "ilbm", "xxxx    xxxx"),
 
index 211c516baa08dfd99cefae31c73cc3260b36c489..3a3ea76c829ddb9f07d5a692a7b809fa4b077460 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -69,6 +69,8 @@ i_img *i_img_pal_new(int x, int y, int ch, int maxpal);
 void   i_img_setmask    (i_img *im,int ch_mask);
 int    i_img_getmask    (i_img *im);
 int    i_img_getchannels(i_img *im);
 void   i_img_setmask    (i_img *im,int ch_mask);
 int    i_img_getmask    (i_img *im);
 int    i_img_getchannels(i_img *im);
+i_img_dim i_img_get_width(i_img *im);
+i_img_dim i_img_get_height(i_img *im);
 
 /* Base functions */
 
 
 /* Base functions */
 
@@ -192,7 +194,7 @@ void i_conv        (i_img *im,const float *coeff,int len);
 void i_unsharp_mask(i_img *im, double stddev, double scale);
 
 /* colour manipulation */
 void i_unsharp_mask(i_img *im, double stddev, double scale);
 
 /* colour manipulation */
-extern int i_convert(i_img *im, i_img *src, const float *coeff, int outchan, int inchan);
+extern i_img *i_convert(i_img *src, const float *coeff, int outchan, int inchan);
 extern void i_map(i_img *im, unsigned char (*maps)[256], unsigned int mask);
 
 float i_img_diff   (i_img *im1,i_img *im2);
 extern void i_map(i_img *im, unsigned char (*maps)[256], unsigned int mask);
 
 float i_img_diff   (i_img *im1,i_img *im2);
index 0cc440c60fa584cfefc32fccd349ee5605341fe9..e9469e14ff3d5db12aa0dd21b0e1b70f659c91e8 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef _DATATYPES_H_
 #define _DATATYPES_H_
 
 #ifndef _DATATYPES_H_
 #define _DATATYPES_H_
 
-#include "imio.h"
 #include "imconfig.h"
 #include "imconfig.h"
+#include "imio.h"
 
 #define MAXCHANNELS 4
 
 
 #define MAXCHANNELS 4
 
@@ -102,9 +102,12 @@ typedef int (*i_f_setcolors_t)(i_img *im, int index, const i_color *colors,
 
 typedef void (*i_f_destroy_t)(i_img *im);
 
 
 typedef void (*i_f_destroy_t)(i_img *im);
 
+typedef int i_img_dim;
+
 struct i_img_ {
   int channels;
 struct i_img_ {
   int channels;
-  int xsize,ysize,bytes;
+  i_img_dim xsize,ysize;
+  size_t bytes;
   unsigned int ch_mask;
   i_img_bits_t bits;
   i_img_type_t type;
   unsigned int ch_mask;
   i_img_bits_t bits;
   i_img_type_t type;
diff --git a/imext.c b/imext.c
index 690cbe25c36fe420c404339f877c87712ed3f874..3e568ca349c187f8823d4e96e393462e1e2b69e3 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -104,6 +104,15 @@ im_ext_funcs imager_function_table =
 
     i_flood_fill_border,
     i_flood_cfill_border,
 
     i_flood_fill_border,
     i_flood_cfill_border,
+
+    /* IMAGER_API_LEVEL 3 functions */
+    i_img_setmask,
+    i_img_getmask,
+    i_img_getchannels,
+    i_img_get_width,
+    i_img_get_height,
+    i_lhead,
+    i_loog
   };
 
 /* in general these functions aren't called by Imager internally, but
   };
 
 /* in general these functions aren't called by Imager internally, but
diff --git a/imext.h b/imext.h
index fd03832844d1fec0d73a391e80762398e21fbcf7..2fca3e44442514aa1ba49b84cccccb5c7803c520 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -191,4 +191,12 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_int_check_image_file_limits(width, height, channels, sample_size) \
   ((im_extt->f_i_int_check_image_file_limits)((width), (height), (channels), (sample_size)))
 
 #define i_int_check_image_file_limits(width, height, channels, sample_size) \
   ((im_extt->f_i_int_check_image_file_limits)((width), (height), (channels), (sample_size)))
 
+#define i_img_setmask(img, mask) ((im_extt->f_i_img_setmask)((img), (mask)))
+#define i_img_getmask(img) ((im_extt->f_i_img_getmask)(img))
+#define i_img_getchannels(img) ((im_extt->f_i_img_getchannels)(img))
+#define i_img_get_width(img) ((im_extt->f_i_img_get_width)(img))
+#define i_img_get_height(img) ((im_extt->f_i_img_get_height)(img))
+#define i_lhead(file, line) ((im_extt->f_i_lhead)((file), (line)))
+#define i_loog (im_extt->f_i_loog)
+
 #endif
 #endif
index 459fb039b7e9c1b44df6c709fd2aa46f4e3e0357..e607d4a3e790c38dc67df2e56d8451bc617902c6 100644 (file)
@@ -18,7 +18,7 @@
  will result in an increment of IMAGER_API_LEVEL.
 */
 
  will result in an increment of IMAGER_API_LEVEL.
 */
 
-#define IMAGER_API_LEVEL 2
+#define IMAGER_API_LEVEL 3
 
 typedef struct {
   int version;
 
 typedef struct {
   int version;
@@ -142,7 +142,16 @@ typedef struct {
   int (*f_i_flood_fill_border)(i_img *im, int seedx, int seedy, const i_color *dcol, const i_color *border);
   int (*f_i_flood_cfill_border)(i_img *im, int seedx, int seedy, i_fill_t *fill, const i_color *border);
 
   int (*f_i_flood_fill_border)(i_img *im, int seedx, int seedy, const i_color *dcol, const i_color *border);
   int (*f_i_flood_cfill_border)(i_img *im, int seedx, int seedy, i_fill_t *fill, const i_color *border);
 
-  /* IMAGER_API_LEVEL 3 functions will be added here */
+  /* IMAGER_API_LEVEL 3 functions */
+  void (*f_i_img_setmask)(i_img *im, int ch_mask);
+  int (*f_i_img_getmask)(i_img *im);
+  int (*f_i_img_getchannels)(i_img *im);
+  i_img_dim (*f_i_img_get_width)(i_img *im);
+  i_img_dim (*f_i_img_get_height)(i_img *im);
+  void (*f_i_lhead)(const char *file, int line_number);
+  void (*f_i_loog)(int level, const char *msg, ...);
+
+  /* IMAGER_API_LEVEL 4 functions will be added here */
 } im_ext_funcs;
 
 #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
 } im_ext_funcs;
 
 #define PERL_FUNCTION_TABLE_NAME "Imager::__ext_func_table"
diff --git a/imio.h b/imio.h
index 682750159d8042013108f224d13531c0b9dfbc53..d78e47327027cc19fe66a26f6768cdcde6df601d 100644 (file)
--- a/imio.h
+++ b/imio.h
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <sys/stat.h>
 
 #include <stdio.h>
 #include <sys/stat.h>
 
+#include "imconfig.h"
 #include "log.h"
 
 typedef struct i_mempool {
 #include "log.h"
 
 typedef struct i_mempool {
index 39927738aca801b675f815dcf70db8e8b5eea357..eaa74737c170faef91262a35afdf7f5142aa5f05 100644 (file)
@@ -1074,6 +1074,79 @@ Sets the given tag to the integer I<idata>
 From: File tags.c
 
 
 From: File tags.c
 
 
+=back
+
+=head2 Uncategorized functions
+
+=over
+
+=item i_img_get_height(im)
+
+
+Returns the height in pixels of the image.
+
+
+=for comment
+From: File image.c
+
+=item i_img_get_width(im)
+
+
+Returns the width in pixels of the image.
+
+
+=for comment
+From: File image.c
+
+=item i_img_getchannels(im)
+
+
+Get the number of channels in I<im>.
+
+
+=for comment
+From: File image.c
+
+=item i_img_getmask(im)
+
+
+Get the image channel mask for I<im>.
+
+
+=for comment
+From: File image.c
+
+=item i_img_setmask(im, ch_mask)
+
+
+Set the image channel mask for I<im> to I<ch_mask>.
+
+
+=for comment
+From: File image.c
+
+
+
+=back
+
+
+=head1 UNDOCUMENTED
+
+The following API functions are undocumented so far, hopefully this
+will change:
+
+=over
+
+=item *
+
+B<i_lhead>
+
+=item *
+
+B<i_loog>
+
+
+
 =back
 
 
 =back
 
 
index a3e7b56a80ac924d93df8b158d39978858a4e357..0c917b765fe24658505d96df606719dee2fc52d5 100644 (file)
@@ -1051,6 +1051,43 @@ Examples:
   $im->settag(name => 'cur_hotspoty', value => 16);
   $im->write(file => 'box.cur');
 
   $im->settag(name => 'cur_hotspoty', value => 16);
   $im->write(file => 'box.cur');
 
+=head2 SGI (RGB, BW)
+
+SGI images, often called by the extensions, RGB or BW, can be stored
+either uncompressed or compressed using an RLE compression.
+
+By default, when saving to an extension of C<rgb>, C<bw>, C<sgi>,
+C<rgba> the file will be saved in SGI format.  The file extension is
+otherwise ignored, so saving a 3-channel image to a C<.bw> file will
+result in a 3-channel image on disk.
+
+The following tags are set when reading a SGI image:
+
+=over
+
+=item *
+
+i_comment - the IMAGENAME field from the image.  Also written to the
+file when writing.
+
+=item *
+
+sgi_pixmin, sgi_pixmax - the PIXMIN and PIXMAX fields from the image.
+On reading image data is expanded from this range to the full range of
+samples in the image.
+
+=item *
+
+sgi_bpc - the number of bytes per sample for the image.  Ignored when
+writing.
+
+=item *
+
+sgi_rle - whether or not the image is compressed.  If this is non-zero
+when writing the image will be compressed.
+
+=back
+
 =head1 ADDING NEW FORMATS
 
 To support a new format for reading, call the register_reader() class
 =head1 ADDING NEW FORMATS
 
 To support a new format for reading, call the register_reader() class
index 3b7d561edb86aab075c7a46a48099c4d1d838679..0689736bf0aae73fce864116ffeaf653edd2967e 100644 (file)
@@ -180,8 +180,8 @@ sub test_image {
   my $blue  = Imager::Color->new(0, 0, 255, 255);
   my $red   = Imager::Color->new(255, 0, 0, 255);
   my $img = Imager->new(xsize => 150, ysize => 150);
   my $blue  = Imager::Color->new(0, 0, 255, 255);
   my $red   = Imager::Color->new(255, 0, 0, 255);
   my $img = Imager->new(xsize => 150, ysize => 150);
-  $img->box(filled => 1, color => $green, box => [ 70, 25, 130, 125 ]);
-  $img->box(filled => 1, color => $blue,  box => [ 20, 25, 80, 125 ]);
+  $img->box(filled => 1, color => $green, box => [ 70, 24, 130, 124 ]);
+  $img->box(filled => 1, color => $blue,  box => [ 20, 26, 80, 126 ]);
   $img->arc(x => 75, y => 75, r => 30, color => $red);
   $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]);
 
   $img->arc(x => 75, y => 75, r => 30, color => $red);
   $img->filter(type => 'conv', coef => [ 0.1, 0.2, 0.4, 0.2, 0.1 ]);
 
@@ -339,6 +339,10 @@ as direct vs paletted, bits per sample are not checked.
 
 Returns a 150x150x3 Imager::ImgRaw test image.
 
 
 Returns a 150x150x3 Imager::ImgRaw test image.
 
+=item test_image()
+
+Returns a 150x150x3 8-bit/sample OO test image.
+
 =item test_image_16()
 
 Returns a 150x150x3 16-bit/sample OO test image.
 =item test_image_16()
 
 Returns a 150x150x3 16-bit/sample OO test image.
diff --git a/rgb.c b/rgb.c
deleted file mode 100644 (file)
index 91c74cb..0000000
--- a/rgb.c
+++ /dev/null
@@ -1,377 +0,0 @@
-#include "imager.h"
-#include "log.h"
-#include "iolayer.h"
-
-#include <stdlib.h>
-#include <errno.h>
-
-
-/*
-=head1 NAME
-
-rgb.c - implements reading and writing sgi image files, uses io layer.
-
-=head1 SYNOPSIS
-
-   io_glue *ig = io_new_fd( fd );
-   i_img *im   = i_readrgb_wiol(ig, -1); // no limit on how much is read
-   // or 
-   io_glue *ig = io_new_fd( fd );
-   return_code = i_writergb_wiol(im, ig); 
-
-=head1 DESCRIPTION
-
-rgb.c implements the basic functions to read and write portable targa
-files.  It uses the iolayer and needs either a seekable source or an
-entire memory mapped buffer.
-
-=head1 FUNCTION REFERENCE
-
-Some of these functions are internal.
-
-=over
-
-=cut
-*/
-
-typedef struct {
-  unsigned short imagic;
-  unsigned char storagetype;
-  unsigned char BPC;
-  unsigned short dimensions;
-  unsigned short xsize, ysize, zsize;
-  unsigned int min, max;
-  char name[80];
-  unsigned int colormap;
-} rgb_header;
-
-typedef struct {
-  int start, length;
-} stlen_pair;
-
-typedef enum { NoInit, Raw, Rle } rle_state;
-
-
-
-typedef struct {
-  int compressed;
-  int bytepp;
-  io_glue *ig;
-} rgb_dest;
-
-
-
-
-
-
-/*
-=item rgb_header_unpack(header, headbuf)
-
-Unpacks the header structure into from buffer and stores
-in the header structure.
-
-    header - header structure
-    headbuf - buffer to unpack from
-
-=cut
-*/
-
-
-static
-void
-rgb_header_unpack(rgb_header *header, unsigned char *headbuf) {
-  header->imagic      = (headbuf[0]<<8) + headbuf[1];
-  header->storagetype = headbuf[2];
-  header->BPC         = headbuf[3];
-  header->dimensions  = (headbuf[4]<<8) + headbuf[5];
-  header->xsize       = (headbuf[6]<<8) + headbuf[7];
-  header->ysize       = (headbuf[8]<<8) + headbuf[9];
-  header->zsize       = (headbuf[10]<<8) + headbuf[11];
-  header->min         = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15];
-  header->max         = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19];
-  memcpy(header->name,headbuf+20,80);
-  header->colormap    = (headbuf[100]<<24) + (headbuf[101]<<16)+(headbuf[102]<<8)+headbuf[103];
-}
-
-#if 0 /* this is currently unused */
-
-/*
-=item rgb_header_pack(header, headbuf)
-
-Packs header structure into buffer for writing.
-
-    header - header structure
-    headbuf - buffer to pack into
-
-=cut
-*/
-
-static
-void
-rgb_header_pack(rgb_header *header, unsigned char headbuf[512]) {
-  
-  header->imagic      = (headbuf[0]<<8) + headbuf[1];
-  header->storagetype = headbuf[2];
-  header->BPC         = headbuf[3];
-  header->dimensions  = (headbuf[4]<<8) + headbuf[5];
-  header->xsize       = (headbuf[6]<<8) + headbuf[7];
-  header->ysize       = (headbuf[8]<<8) + headbuf[9];
-  header->zsize       = (headbuf[10]<<8) + headbuf[11];
-  header->min         = (headbuf[12]<<24) + (headbuf[13]<<16)+(headbuf[14]<<8)+headbuf[15];
-  header->max         = (headbuf[16]<<24) + (headbuf[17]<<16)+(headbuf[18]<<8)+headbuf[19];
-  memcpy(header->name,headbuf+20,80);
-  header->colormap    = (headbuf[100]<<24) + (headbuf[101]<<16)+(headbuf[102]<<8)+headbuf[103];
-
-}
-
-
-
-
-
-/*
-=item rgb_dest_write(s, buf, pixels)
-
-Writes pixels from buf to destination s.  Takes care of compressing if the
-destination is compressed.
-
-    s - data destination
-    buf - source buffer
-    pixels - number of pixels to put write to destination
-
-=cut
-*/
-
-static
-int
-rgb_dest_write(rgb_dest *s, unsigned char *buf, size_t pixels) {
-  return -1;
-}
-
-#endif
-
-
-
-
-
-
-/*
-=item i_readrgb_wiol(ig, length)
-
-Read in an image from the iolayer data source and return the image structure to it.
-Returns NULL on error.
-
-   ig     - io_glue object
-   length - maximum length to read from data source, before closing it -1 
-            signifies no limit.
-
-=cut
-*/
-
-i_img *
-i_readrgb_wiol(io_glue *ig, int length) {
-  i_img *img;
-  int y, c,i;
-  int width, height, channels;
-  unsigned long maxlen;
-
-  int savemask;
-  
-  rgb_header header;
-  unsigned char headbuf[512];
-  unsigned char *databuf;
-  unsigned long *starttab, *lengthtab;
-  i_color *linebuf = NULL;
-  i_mempool mp;
-
-  mm_log((1,"i_readrgb(ig %p, length %d)\n", ig, length));
-  i_clear_error();
-  i_mempool_init(&mp);
-  
-  io_glue_commit_types(ig);
-
-  if (ig->readcb(ig, headbuf, 512) != 512) {
-    i_push_error(errno, "could not read SGI rgb header");
-    return NULL;
-  }
-
-  rgb_header_unpack(&header, headbuf);
-  
-  
-  mm_log((1,"imagic:         %d\n", header.imagic));
-  mm_log((1,"storagetype:    %d\n", header.storagetype));
-  mm_log((1,"BPC:            %d\n", header.BPC));
-  mm_log((1,"dimensions:     %d\n", header.dimensions));
-  mm_log((1,"xsize:          %d\n", header.xsize));
-  mm_log((1,"ysize:          %d\n", header.ysize));
-  mm_log((1,"zsize:          %d\n", header.zsize));
-  mm_log((1,"min:            %d\n", header.min));
-  mm_log((1,"max:            %d\n", header.max));
-  mm_log((1,"name [skipped]\n"));
-  mm_log((1,"colormap:       %d\n", header.colormap));
-
-  if (header.colormap != 0) {
-    i_push_error(0, "SGI rgb image has a non zero colormap entry - obsolete format");
-    return NULL;
-  }
-
-  if (header.storagetype != 0 && header.storagetype != 1) {
-    i_push_error(0, "SGI rgb image has has invalid storage field");
-    return NULL;
-  }
-
-  width    = header.xsize;
-  height   = header.ysize;
-  channels = header.zsize;
-
-  img = i_img_empty_ch(NULL, width, height, channels);
-  if (!img)
-    return NULL;
-  
-  i_tags_add(&img->tags, "rgb_namestr", 0, header.name, 80, 0);
-  i_tags_add(&img->tags, "i_format", 0, "rgb", -1, 0);
-
-  switch (header.storagetype) {
-  case 0: /* uncompressed */
-    
-    linebuf   = i_mempool_alloc(&mp, width*sizeof(i_color));
-    databuf   = i_mempool_alloc(&mp, width);
-
-    savemask = i_img_getmask(img);
-
-    for(c=0; c<channels; c++) {
-      i_img_setmask(img, 1<<c);
-      for(y=0; y<height; y++) {
-       int x;
-       
-       if (ig->readcb(ig, databuf, width) != width) {
-         i_push_error(0, "SGI rgb: cannot read");
-         goto ErrorReturn;
-       }
-
-       for(x=0; x<width; x++)
-         linebuf[x].channel[c] = databuf[x];
-       
-       i_plin(img, 0, width, height-1-y, linebuf);
-      }
-    }
-    i_img_setmask(img, savemask);
-    break;
-  case 1: /* RLE compressed */
-    
-    databuf   = i_mempool_alloc(&mp, height*channels*4);
-    starttab  = i_mempool_alloc(&mp, height*channels*sizeof(unsigned long));
-    lengthtab = i_mempool_alloc(&mp, height*channels*sizeof(unsigned long));
-    linebuf   = i_mempool_alloc(&mp, width*sizeof(i_color));
-    
-    /* Read offset table */
-    if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) goto ErrorReturn;
-    for(i=0; i<height*channels; i++) starttab[i] = 
-                                      (databuf[i*4]<<24) | 
-                                      (databuf[i*4+1]<<16) | 
-                                      (databuf[i*4+2]<<8) |
-                                      (databuf[i*4+3]);
-
-
-    /* Read length table */
-    if (ig->readcb(ig, databuf, height*channels*4) != height*channels*4) goto ErrorReturn;
-    for(i=0; i<height*channels; i++) lengthtab[i] = 
-                                      (databuf[i*4]<<24)+
-                                      (databuf[i*4+1]<<16)+
-                                      (databuf[i*4+2]<<8)+
-                                      (databuf[i*4+3]);
-
-    mm_log((3, "Offset/length table:\n"));
-    for(i=0; i<height*channels; i++)
-      mm_log((3, "%d: %d/%d\n", i, starttab[i], lengthtab[i]));
-
-
-    /* Find max spanlength if someone is making very badly formed RLE data */
-    maxlen = 0;
-    for(y=0; y<height; y++) maxlen = (maxlen>lengthtab[y])?maxlen:lengthtab[y];
-
-    mm_log((1, "maxlen for an rle buffer: %d\n", maxlen));
-
-    databuf = i_mempool_alloc(&mp, maxlen);
-
-    for(y=0; y<height; y++) {
-      for(c=0; c<channels; c++) {
-       unsigned long iidx = 0, oidx = 0, span = 0;
-       unsigned char cval = 0;
-       int rle = 0;
-       int ci = height*c+y;
-       int datalen = lengthtab[ci];
-
-       if (ig->seekcb(ig, starttab[ci], SEEK_SET) != starttab[ci]) {
-         i_push_error(0, "SGI rgb: cannot seek");
-         goto ErrorReturn;
-       }
-       if (ig->readcb(ig, databuf, datalen) != datalen) {
-         i_push_error(0, "SGI rgb: cannot read");
-         goto ErrorReturn;
-       }
-
-       /*
-         mm_log((1, "Buffer length %d\n", datalen));
-         for(i=0; i<datalen; i++) 
-         mm_log((1, "0x%x\n", databuf[i]));
-       */
-
-       while( iidx <= datalen && oidx < width ) {
-         if (!span) {
-           span = databuf[iidx] & 0x7f;
-           rle  = !(databuf[iidx++] & 0x80);
-           /*      mm_log((1,"new span %d, rle %d\n", span, rle)); */
-           if (rle) {
-             if (iidx==datalen) {
-               i_push_error(0, "SGI rgb: bad rle data");
-               goto ErrorReturn;
-             }
-             cval = databuf[iidx++];
-             /* mm_log((1, "rle value %d\n", cval)); */
-           }
-         }
-         linebuf[oidx++].channel[c] = rle ? cval : databuf[iidx++];
-         span--;
-         /*
-           mm_log((1,"iidx=%d/%d, oidx=%d/%d, linebuf[%d].channel[%d] %d\n", iidx-1, datalen, oidx-1, width, oidx-1, c, linebuf[oidx-1].channel[c]));
-         */
-       }
-      }
-      i_plin(img, 0, width, height-1-y, linebuf);
-    }
-    
-    break;
-  }
-
-  i_tags_add(&img->tags, "i_format", 0, "rgb", -1, 0);
-
-  i_mempool_destroy(&mp);
-  return img;
-
- ErrorReturn:
-  i_mempool_destroy(&mp);
-  if (img) i_img_destroy(img);
-  return NULL;
-}
-
-
-
-/*
-=item i_writergb_wiol(img, ig)
-
-Writes an image in targa format.  Returns 0 on error.
-
-   img    - image to store
-   ig     - io_glue object
-
-=cut
-*/
-
-undef_int
-i_writergb_wiol(i_img *img, io_glue *ig, int wierdpack, int compress, char *idstring, size_t idlen) {
-  i_clear_error();
-  i_push_error(0, "writing SGI RGB files is not implemented");
-
-  return 0;
-}
-
index 42d64755d788bfa115cd1547444279f555d8b673..041d5d2599245b4f7ed30a133bfa2afb03792ecb 100644 (file)
@@ -91,11 +91,11 @@ probe_ok(<<ICO, "cur", "Windows Cursor");
 00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
 ICO
 
 00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
 ICO
 
-probe_ok(<<RGB, "rgb", "SGI RGB");
+probe_ok(<<SGI, "sgi", "SGI RGB");
 01 DA 01 01 00 03 00 96 00 96 00 03 00 00 00 00 
 00 00 00 FF 00 00 00 00 6E 6F 20 6E 61 6D 65 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 01 DA 01 01 00 03 00 96 00 96 00 03 00 00 00 00 
 00 00 00 FF 00 00 00 00 6E 6F 20 6E 61 6D 65 00
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-RGB
+SGI
 
 probe_ok(<<ILBM, "ilbm", "ILBM");
 46 4F 52 4D 00 00 60 7A 49 4C 42 4D 42 4D 48 44
 
 probe_ok(<<ILBM, "ilbm", "ILBM");
 46 4F 52 4D 00 00 60 7A 49 4C 42 4D 42 4D 48 44
index 3661b45ef63e49e9a3b51d63156f2349ab00525d..dadc9dc1f06cbd4010ad12cdc297e1a103ac8afe 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 use strict;
 use Imager qw(:all :handy);
 #!perl -w
 use strict;
 use Imager qw(:all :handy);
-use Test::More tests=>19;
+use Test::More tests=>21;
 
 Imager::init("log"=>'testout/t67convert.log');
 
 
 Imager::init("log"=>'testout/t67convert.log');
 
@@ -10,13 +10,13 @@ my $imbase = Imager::ImgRaw::new(200,300,3);
 # first a basic test, make sure the basic things happen ok
 # make a 1 channel image from the above (black) image
 # but with 1 as the 'extra' value
 # first a basic test, make sure the basic things happen ok
 # make a 1 channel image from the above (black) image
 # but with 1 as the 'extra' value
-my $imnew = Imager::i_img_new();
 SKIP:
 {
 SKIP:
 {
+  my $im_white = i_convert($imbase, [ [ 0, 0, 0, 1 ] ]);
   skip("convert to white failed", 3)
   skip("convert to white failed", 3)
-    unless ok(i_convert($imnew, $imbase, [ [ 0, 0, 0, 1 ] ]), "convert to white");
+    unless ok($im_white, "convert to white");
 
 
-  my ($w, $h, $ch) = i_img_info($imnew);
+  my ($w, $h, $ch) = i_img_info($im_white);
 
   # the output image should now have one channel
   is($ch, 1, "one channel image now");
 
   # the output image should now have one channel
   is($ch, 1, "one channel image now");
@@ -24,7 +24,7 @@ SKIP:
   ok($w == 200 && $h == 300, "check converted size is the same");
 
   # should be a white image now, let's check
   ok($w == 200 && $h == 300, "check converted size is the same");
 
   # should be a white image now, let's check
-  my $c = Imager::i_get_pixel($imnew, 20, 20);
+  my $c = Imager::i_get_pixel($im_white, 20, 20);
   my @c = $c->rgba;
   print "# @c\n";
   is($c[0], 255, "check image is white");
   my @c = $c->rgba;
   print "# @c\n";
   is($c[0], 255, "check image is white");
@@ -49,21 +49,23 @@ SKIP:
 }
 
 # test against 16-bit/sample images
 }
 
 # test against 16-bit/sample images
-my $im16targ = Imager::i_img_16_new(200, 300, 3);
 SKIP:
 {
 SKIP:
 {
+  my $imbase16 = Imager::i_img_16_new(200, 200, 3);
+  
+  my $im16targ = i_convert($imbase16, [ [ 0, 0, 0, 1 ],
+                                       [ 0, 0, 0, 0 ],
+                                       [ 0, 0, 0, 0 ] ]);
   skip("could not convert 16-bit image", 2)
   skip("could not convert 16-bit image", 2)
-    unless ok(i_convert($im16targ, $imbase, [ [ 0, 0, 0, 1 ],
-                                              [ 0, 0, 0, 0 ],
-                                              [ 0, 0, 0, 0 ] ]),
-              "convert 16/bit sample image");
+    unless ok($im16targ, "convert 16/bit sample image");
   # image should still be 16-bit
   is(Imager::i_img_bits($im16targ), 16, "Image still 16-bit/sample");
   # make sure that it's roughly red
   my $c = Imager::i_gpixf($im16targ, 0, 0);
   my @ch = $c->rgba;
   ok(abs($ch[0] - 1) <= 0.0001 && abs($ch[1]) <= 0.0001 && abs($ch[2]) <= 0.0001,
   # image should still be 16-bit
   is(Imager::i_img_bits($im16targ), 16, "Image still 16-bit/sample");
   # make sure that it's roughly red
   my $c = Imager::i_gpixf($im16targ, 0, 0);
   my @ch = $c->rgba;
   ok(abs($ch[0] - 1) <= 0.0001 && abs($ch[1]) <= 0.0001 && abs($ch[2]) <= 0.0001,
-     "image roughly red");
+     "image roughly red")
+    or print "# @ch\n";
 }
 
 # test against palette based images
 }
 
 # test against palette based images
@@ -74,14 +76,14 @@ ok($blackindex, "add black to paletted");
 for my $y (0..299) {
   Imager::i_ppal($impal, 0, $y, ($blackindex) x 200);
 }
 for my $y (0..299) {
   Imager::i_ppal($impal, 0, $y, ($blackindex) x 200);
 }
-my $impalout = Imager::i_img_pal_new(200, 300, 3, 256);
+
 SKIP:
 {
 SKIP:
 {
+  my $impalout = i_convert($impal, [ [ 0, 0, 0, 0 ],
+                                    [ 0, 0, 0, 1 ],
+                                    [ 0, 0, 0, 0 ] ]);
   skip("could not convert paletted", 3)
   skip("could not convert paletted", 3)
-    unless ok(i_convert($impalout, $impal, [ [ 0, 0, 0, 0 ],
-                                   [ 0, 0, 0, 1 ],
-                                   [ 0, 0, 0, 0 ] ]),
-             "convert paletted");
+    unless ok($impalout, "convert paletted");
   is(Imager::i_img_type($impalout), 1, "image still paletted");
   is(Imager::i_colorcount($impalout), 1, "still only one colour");
   my $c = Imager::i_getcolors($impalout, $blackindex);
   is(Imager::i_img_type($impalout), 1, "image still paletted");
   is(Imager::i_colorcount($impalout), 1, "still only one colour");
   my $c = Imager::i_getcolors($impalout, $blackindex);
@@ -107,3 +109,11 @@ SKIP:
   cmp_ok($warning, '=~', 'void', "correct warning");
   cmp_ok($warning, '=~', 't67convert\\.t', "correct file");
 }
   cmp_ok($warning, '=~', 'void', "correct warning");
   cmp_ok($warning, '=~', 't67convert\\.t', "correct file");
 }
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=28492
+  my $im = Imager->new(xsize => 20, ysize => 20, channels => 3, 
+                      bits => 'double');
+  is($im->bits, 'double', 'check source bits');
+  my $conv = $im->convert(preset => 'grey');
+  is($conv->bits, 'double', 'make sure result has extra bits');
+}