Adds reading capabilities for certain variants of targa, writer code has not been
authorArnar Mar Hrafnkelsson <addi@cpan.org>
Sun, 16 Sep 2001 09:01:54 +0000 (09:01 +0000)
committerArnar Mar Hrafnkelsson <addi@cpan.org>
Sun, 16 Sep 2001 09:01:54 +0000 (09:01 +0000)
done yet.

Imager.pm
Imager.xs
MANIFEST
Makefile.PL
feat.h
image.h
tga.c [new file with mode: 0644]

index dfa6bb7..3a85b12 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -809,7 +809,7 @@ sub read {
     $self->{ERRSTR}='format not supported'; return undef;
   }
 
-  my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1);
+  my %iolready=(jpeg=>1, png=>1, tiff=>1, pnm=>1, raw=>1, bmp=>1, tga=>1);
 
   if ($iolready{$input{type}}) {
     # Setup data source
@@ -860,6 +860,16 @@ sub read {
       $self->{DEBUG} && print "loading a bmp file\n";
     }
 
+    if ( $input{type} eq 'tga' ) {
+      $self->{IMG}=i_readtga_wiol( $IO, -1 ); # Fixme, check if that length parameter is ever needed
+      if ( !defined($self->{IMG}) ) {
+       $self->{ERRSTR}=$self->_error_as_msg();
+#      $self->{ERRSTR}='unable to read tga image';
+       return undef;
+      }
+      $self->{DEBUG} && print "loading a tga file\n";
+    }
+
     if ( $input{type} eq 'raw' ) {
       my %params=(datachannels=>3,storechannels=>3,interleave=>1,%input);
 
@@ -953,7 +963,7 @@ sub write {
             fax_fine=>1, @_);
   my ($fh, $rc, $fd, $IO);
 
-  my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1 ); # this will be SO MUCH BETTER once they are all in there
+  my %iolready=( tiff=>1, raw=>1, png=>1, pnm=>1, bmp=>1, jpeg=>1, tga=>1 ); # this will be SO MUCH BETTER once they are all in there
 
   unless ($self->{IMG}) { $self->{ERRSTR}='empty input image'; return undef; }
 
@@ -1021,6 +1031,13 @@ sub write {
        return undef;
       }
       $self->{DEBUG} && print "writing a bmp file\n";
+    } elsif ( $input{type} eq 'tga' ) {
+      if ( !i_writetga_wiol($self->{IMG}, $IO) ) {
+       $self->{ERRSTR}=$self->_error_as_msg();
+#      $self->{ERRSTR}='unable to write tga image';
+       return undef;
+      }
+      $self->{DEBUG} && print "writing a tga file\n";
     }
 
     if (exists $input{'data'}) {
@@ -2020,6 +2037,7 @@ sub def_guess_type {
   return 'pnm'  if ($ext =~ m/^p[pgb]m$/);
   return 'png'  if ($ext eq "png");
   return 'bmp'  if ($ext eq "bmp" || $ext eq "dib");
+  return 'tga'  if ($ext eq "tga");
   return 'gif'  if ($ext eq "gif");
   return ();
 }
@@ -3814,7 +3832,7 @@ the i_aspect_only tag is non-zero.
 
 =back
 
-The following tags are set when reading a Windows BMP file is read:
+The following tags are set when a Windows BMP file is read:
 
 =over
 
index 3299e07..db3bc5d 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -1746,6 +1746,18 @@ Imager::ImgRaw
 i_readbmp_wiol(ig)
         Imager::IO     ig
 
+
+undef_int
+i_writetga_wiol(im,ig)
+    Imager::ImgRaw     im
+        Imager::IO     ig
+
+Imager::ImgRaw
+i_readtga_wiol(ig, length)
+        Imager::IO     ig
+               int     length
+
+
 Imager::ImgRaw
 i_scaleaxis(im,Value,Axis)
     Imager::ImgRaw     im
index 4121d7a..33baea2 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -5,6 +5,7 @@ MANIFEST
 README
 Makefile.PL
 bmp.c           Reading and writing Windows BMP files
+tga.c           Reading and writing Targa files
 color.c         Color translation and handling
 conv.c
 convert.c
index d144c6d..b4d2817 100644 (file)
@@ -63,7 +63,7 @@ if (defined $Config{'d_dlsymun'}) { $OSDEF  .= ' -DDLSYMUN'; }
           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 color.o fills.o);
+           bmp.o tga.o color.o fills.o);
 
 %opts=(
        'NAME'         => 'Imager',
diff --git a/feat.h b/feat.h
index c82f953..ade7a92 100644 (file)
--- a/feat.h
+++ b/feat.h
@@ -28,5 +28,6 @@ static char *i_format_list[]={
   "raw",
   "pnm",
   "bmp",
+  "tga",
   NULL};
 
diff --git a/image.h b/image.h
index 16686c9..c623694 100644 (file)
--- a/image.h
+++ b/image.h
@@ -547,9 +547,12 @@ undef_int i_writeraw_wiol(i_img* im, io_glue *ig);
 i_img   * i_readpnm_wiol(io_glue *ig, int length);
 undef_int i_writeppm_wiol(i_img *im, io_glue *ig);
 
-extern int i_writebmp_wiol(i_img *im, io_glue *ig);
+extern int    i_writebmp_wiol(i_img *im, io_glue *ig);
 extern i_img *i_readbmp_wiol(io_glue *ig);
 
+i_img   * i_readtga_wiol(io_glue *ig, int length);
+undef_int i_writetga_wiol(i_img *im, io_glue *ig);
+
 i_img * i_scaleaxis(i_img *im, float Value, int Axis);
 i_img * i_scale_nn(i_img *im, float scx, float scy);
 i_img * i_haar(i_img *im);
diff --git a/tga.c b/tga.c
new file mode 100644 (file)
index 0000000..0f467ba
--- /dev/null
+++ b/tga.c
@@ -0,0 +1,344 @@
+#include "image.h"
+#include "io.h"
+#include "log.h"
+#include "iolayer.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+
+/*
+=head1 NAME
+
+tga.c - implements reading and writing targa files, uses io layer.
+
+=head1 SYNOPSIS
+
+   io_glue *ig = io_new_fd( fd );
+   i_img *im   = i_readtga_wiol(ig, -1); // no limit on how much is read
+   // or 
+   io_glue *ig = io_new_fd( fd );
+   return_code = i_writetga_wiol(im, ig); 
+
+=head1 DESCRIPTION
+
+tga.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 4
+
+=cut
+*/
+
+
+#define BSIZ 1024
+
+
+
+typedef struct {
+  char  idlength;
+  char  colourmaptype;
+  char  datatypecode;
+  short int colourmaporigin;
+  short int colourmaplength;
+  char  colourmapdepth;
+  short int x_origin;
+  short int y_origin;
+  short width;
+  short height;
+  char  bitsperpixel;
+  char  imagedescriptor;
+} tga_header;
+
+
+
+/*
+=item i_readtga_wiol(ig, length)
+
+Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
+
+   ig     - io_glue object
+   length - maximum length to read from data source, before closing it -1 
+            signifies no limit.
+
+=cut
+*/
+
+i_img *
+i_readtga_wiol(io_glue *ig, int length) {
+  i_img* img;
+  int type;
+  int y, i;
+  int width, height, channels;
+  char *idstring;
+  unsigned char *uc;
+  i_color val;
+
+  tga_header header;
+  size_t palbsize;
+  unsigned char *palbuf;
+  unsigned char headbuf[18];
+  unsigned char *linebuf;
+
+  i_clear_error();
+
+  mm_log((1,"i_readtga(ig %p, length %d)\n", ig, length));
+  
+  io_glue_commit_types(ig);
+
+  if (ig->readcb(ig, &headbuf, 18) != 18) {
+    i_push_error(errno, "could not read targa header");
+    return NULL;
+  }
+
+  header.idlength        = headbuf[0];
+  header.colourmaptype   = headbuf[1];
+  header.datatypecode    = headbuf[2];
+  header.colourmaporigin = headbuf[4] << 8 + headbuf[3];
+  header.colourmaplength = headbuf[6] << 8 + headbuf[5];
+  header.colourmapdepth  = headbuf[7];
+  header.x_origin        = headbuf[9] << 8 + headbuf[8];
+  header.y_origin        = headbuf[11] << 8 + headbuf[10];
+  header.width           = (headbuf[13] << 8) + headbuf[12];
+  header.height          = (headbuf[15] << 8) + headbuf[14];
+  header.bitsperpixel    = headbuf[16];
+  header.imagedescriptor = headbuf[17];
+
+  mm_log((1,"Id length:         %d\n",header.idlength));
+  mm_log((1,"Colour map type:   %d\n",header.colourmaptype));
+  mm_log((1,"Image type:        %d\n",header.datatypecode));
+  mm_log((1,"Colour map offset: %d\n",header.colourmaporigin));
+  mm_log((1,"Colour map length: %d\n",header.colourmaplength));
+  mm_log((1,"Colour map depth:  %d\n",header.colourmapdepth));
+  mm_log((1,"X origin:          %d\n",header.x_origin));
+  mm_log((1,"Y origin:          %d\n",header.y_origin));
+  mm_log((1,"Width:             %d\n",header.width));
+  mm_log((1,"Height:            %d\n",header.height));
+  mm_log((1,"Bits per pixel:    %d\n",header.bitsperpixel));
+  mm_log((1,"Descriptor:        %d\n",header.imagedescriptor));
+
+
+  if (header.idlength) {
+    idstring = mymalloc(header.idlength+1);
+    if (ig->readcb(ig, idstring, header.idlength) != header.idlength) {
+      i_push_error(errno, "short read on targa idstring");
+      return NULL;
+    }
+    myfree(idstring); /* Move this later, once this is stored in a tag */
+  }
+
+  width = header.width;
+  height = header.height;
+  
+  /* Set tags here */
+
+  /* Only RGB for now, eventually support all formats */
+  switch (header.datatypecode) {
+
+  case 0: /* No data in image */
+    i_push_error(0, "Targa image contains no image data");
+    return NULL;
+    break;
+
+  case 1: /* Uncompressed, color-mapped images */
+    if (header.imagedescriptor != 0) {
+      i_push_error(0, "Targa: Imagedescriptor not 0, not supported internal format");
+      return NULL;
+    }
+    
+    if (header.bitsperpixel != 8) {
+      i_push_error(0, "Targa: bpp is not 8, unsupported.");
+      return NULL;
+    }
+    
+    mm_log((1, "Uncompressed color-mapped image\n"));
+    if (header.colourmapdepth != 24) {
+      i_push_error(0, "Colourmap is not 24 bit");
+      return NULL;
+    }
+    channels = 3;
+
+    img = i_img_pal_new(width, height, channels, 256);
+
+    /* Read in the palette */
+
+    palbsize = header.colourmaplength*channels;
+    palbuf = mymalloc(palbsize);
+    
+    if (ig->readcb(ig, palbuf, palbsize) != palbsize) {
+      i_push_error(errno, "could not read targa colourmap");
+      return NULL;
+    }
+    
+    /* populate the palette of the new image */
+    for(i=0; i<header.colourmaplength; i++) {
+      i_color col;
+      col.rgba.r = palbuf[i*channels+2];
+      col.rgba.g = palbuf[i*channels+1];
+      col.rgba.b = palbuf[i*channels+0];
+      i_addcolors(img, &col, 1);
+    }
+    myfree(palbuf);
+
+
+    /* read image data in */
+    linebuf = mymalloc(width);
+    for(y=height-1;y>=0;y--) {
+      if (ig->readcb(ig, linebuf, width) != width) {
+       i_push_error(errno, "read for targa data failed");
+       myfree(linebuf);
+       return NULL;
+      }
+      i_ppal(img, 0, width, y, linebuf);
+    }
+    
+    return img;
+    break;
+    
+  case 2:  /* Uncompressed, RGB images */
+    mm_log((1, "Uncompressed RGB image\n"));
+    channels = 3;
+    /* Do stuff */
+    break;
+  case 3: /* Uncompressed, black and white images */
+    i_push_error(0, "Targa black and white subformat is not supported");
+    return NULL;
+    break;
+  case 9: /* Runlength encoded, color-mapped images */
+    i_push_error(0, "Targa runlength coded colormapped subformat is not supported");
+    return NULL;
+    break;
+  case 10: /* Runlength encoded, RGB images */
+    channels = 3;
+    /* Do stuff */
+    break;
+  case 11: /* Compressed, black and white images */
+    i_push_error(0, "Targa compressed black and white subformat is not supported");
+    return NULL;
+    break;
+  case 32: /* Compressed color-mapped, Huffman, Delta and runlength */
+    i_push_error(0, "Targa Huffman/delta/rle subformat is not supported");
+    return NULL;
+    break;
+  case 33: /* Compressed color-mapped, Huffman, Delta and runlength */
+    i_push_error(0, "Targa Huffman/delta/rle/quadtree subformat is not supported");
+    return NULL;
+    break;
+  default: /* All others which we don't know which might be */
+    i_push_error(0, "Unknown targa format");
+    return NULL;
+    break;
+  }
+  return NULL;
+}
+
+
+
+
+
+
+
+
+
+undef_int
+i_writetga_wiol(i_img *im, io_glue *ig) {
+  char header[255];
+  int rc;
+  writep write_func;
+
+  mm_log((1,"i_writeppm(im %p, ig %p)\n", im, ig));
+  i_clear_error();
+
+  /* Add code to get the filename info from the iolayer */
+  /* Also add code to check for mmapped code */
+
+  io_glue_commit_types(ig);
+
+  if (im->channels == 3) {
+    sprintf(header,"P6\n#CREATOR: Imager\n%d %d\n255\n",im->xsize,im->ysize);
+    if (ig->writecb(ig,header,strlen(header))<0) {
+      i_push_error(errno, "could not write ppm header");
+      mm_log((1,"i_writeppm: unable to write ppm header.\n"));
+      return(0);
+    }
+
+    if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+      rc = ig->writecb(ig,im->idata,im->bytes);
+    }
+    else {
+      unsigned char *data = mymalloc(3 * im->xsize);
+      if (data != NULL) {
+        int y = 0;
+        int x, ch;
+        unsigned char *p;
+        static int rgb_chan[3] = { 0, 1, 2 };
+
+        rc = 0;
+        while (y < im->ysize && rc >= 0) {
+          i_gsamp(im, 0, im->xsize, y, data, rgb_chan, 3);
+          rc = ig->writecb(ig, data, im->xsize * 3);
+        }
+        myfree(data);
+      }
+      else {
+        i_push_error(0, "Out of memory");
+        return 0;
+      }
+    }
+    if (rc<0) {
+      i_push_error(errno, "could not write ppm data");
+      mm_log((1,"i_writeppm: unable to write ppm data.\n"));
+      return(0);
+    }
+  }
+  else if (im->channels == 1) {
+    sprintf(header, "P5\n#CREATOR: Imager\n%d %d\n255\n",
+           im->xsize, im->ysize);
+    if (ig->writecb(ig,header, strlen(header)) < 0) {
+      i_push_error(errno, "could not write pgm header");
+      mm_log((1,"i_writeppm: unable to write pgm header.\n"));
+      return(0);
+    }
+
+    if (!im->virtual && im->bits == i_8_bits && im->type == i_direct_type) {
+      rc=ig->writecb(ig,im->idata,im->bytes);
+    }
+    else {
+      unsigned char *data = mymalloc(im->xsize);
+      if (data != NULL) {
+        int y = 0;
+        int x, ch;
+        int chan = 0;
+        unsigned char *p;
+
+        rc = 0;
+        while (y < im->ysize && rc >= 0) {
+          i_gsamp(im, 0, im->xsize, y, data, &chan, 1);
+          rc = ig->writecb(ig, data, im->xsize);
+        }
+        myfree(data);
+      }
+      else {
+        i_push_error(0, "Out of memory");
+        return 0;
+      }
+    }
+    if (rc<0) {
+      i_push_error(errno, "could not write pgm data");
+      mm_log((1,"i_writeppm: unable to write pgm data.\n"));
+      return(0);
+    }
+  }
+  else {
+    i_push_error(0, "can only save 1 or 3 channel images to pnm");
+    mm_log((1,"i_writeppm: ppm/pgm is 1 or 3 channel only (current image is %d)\n",im->channels));
+    return(0);
+  }
+
+  return(1);
+}