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
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
===========
$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);
$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;
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
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>,
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">
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>
+SGI files - L<Imager::Files/"SGI (RGB, BW)">
+
sharpen - L<Imager::Filters/unsharpmask>, L<Imager::Filters/conv>
size, image - L<Imager::ImageTypes/getwidth>,
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
+ AV *avmain
PREINIT:
float *coeff;
int outchan;
int inchan;
- AV *avmain;
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;
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
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
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
regops.perl
render.im
rendert.h Buffer rendering engine types
-rgb.c Reading and writing SGI rgb files
rotate.c
rubthru.im
samples/README
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} =
--- /dev/null
+#!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);
+
+
--- /dev/null
+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
--- /dev/null
+#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;
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#!perl -w
+use strict;
+use Test::More tests => 1;
+
+use_ok('Imager::File::SGI');
--- /dev/null
+#!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;
+}
--- /dev/null
+#!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;
+ }
+ )
+}
/*
-=item i_convert(im, src, coeff, outchan, inchan)
+=item i_convert(src, coeff, outchan, inchan)
Converts the image src into another image.
=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];
+ 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();
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 *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);
myfree(colors);
}
- return 1;
+ return im;
}
/*
"pnm",
"bmp",
"tga",
- "rgb",
"ifs",
NULL};
/*
=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
/*
=item i_img_getmask(im)
+=synopsis mask = i_img_getmask(img);
+
Get the image channel mask for I<im>.
=cut
/*
=item i_img_getchannels(im)
+=synopsis channels = i_img_getchannels(img);
+
Get the number of channels in I<im>.
=cut
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)
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"),
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 */
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);
#ifndef _DATATYPES_H_
#define _DATATYPES_H_
-#include "imio.h"
#include "imconfig.h"
+#include "imio.h"
#define MAXCHANNELS 4
typedef void (*i_f_destroy_t)(i_img *im);
+typedef int i_img_dim;
+
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;
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
#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
will result in an increment of IMAGER_API_LEVEL.
*/
-#define IMAGER_API_LEVEL 2
+#define IMAGER_API_LEVEL 3
typedef struct {
int version;
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"
#include <stdio.h>
#include <sys/stat.h>
+#include "imconfig.h"
#include "log.h"
typedef struct i_mempool {
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
$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
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 ]);
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.
+++ /dev/null
-#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;
-}
-
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
-RGB
+SGI
probe_ok(<<ILBM, "ilbm", "ILBM");
46 4F 52 4D 00 00 60 7A 49 4C 42 4D 42 4D 48 44
#!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');
# 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:
{
+ my $im_white = i_convert($imbase, [ [ 0, 0, 0, 1 ] ]);
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");
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");
}
# test against 16-bit/sample images
-my $im16targ = Imager::i_img_16_new(200, 300, 3);
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)
- 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 roughly red");
+ "image roughly red")
+ or print "# @ch\n";
}
# test against palette based images
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:
{
+ my $impalout = i_convert($impal, [ [ 0, 0, 0, 0 ],
+ [ 0, 0, 0, 1 ],
+ [ 0, 0, 0, 0 ] ]);
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);
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');
+}