diff options
Diffstat (limited to 'converter/other/sgitopnm.c')
-rw-r--r-- | converter/other/sgitopnm.c | 747 |
1 files changed, 404 insertions, 343 deletions
diff --git a/converter/other/sgitopnm.c b/converter/other/sgitopnm.c index 0d26bb9c..6fd4efcf 100644 --- a/converter/other/sgitopnm.c +++ b/converter/other/sgitopnm.c @@ -5,6 +5,11 @@ ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp) ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC ** +** The definitive document describing the SGI image file format, +** SGI Image File Format Version 1.00 is available from +** ftp://ftp.sgi.com/graphics/grafica/sgiimage.html +** +** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that @@ -12,314 +17,451 @@ ** documentation. This software is provided "as is" without express or ** implied warranty. ** -** 29Jan94: first version -** 08Feb94: minor bugfix -** 29Jul00: added -channel option (smar@reptiles.org) */ + + +#include <unistd.h> +#include <limits.h> +#include "pm_c_util.h" +#include "mallocvar.h" +#include "shhopt.h" #include "pnm.h" #include "sgi.h" -#include "mallocvar.h" -#ifndef VMS -#include <unistd.h> -#endif -/*#define DEBUG*/ +#define MAX_ZSIZE 256 -#ifndef SEEK_SET -#define SEEK_SET 0 -#endif +struct CmdlineInfo { + /* All the information the user supplied in the command line, + in a form easy for the program to use. + */ + const char * inputFileName; /* '-' if stdin */ + unsigned int verbose; + unsigned int channelSpec; + unsigned int channel; +}; -/* entry in RLE offset table */ -typedef struct { - long start; /* offset in file */ - long length; /* length of compressed scanline */ -} TabEntry; -typedef short ScanElem; -typedef ScanElem * ScanLine; - -/* prototypes */ -static unsigned char get_byte ARGS(( FILE* f )); -static long get_big_long ARGS((FILE *f)); -static short get_big_short ARGS((FILE *f)); -static short get_byte_as_short ARGS((FILE *f)); -static void readerr ARGS((FILE *f)); -static const char * -compression_name(char compr); -static void read_bytes ARGS((FILE *ifp, int n, char *buf)); -static Header * read_header ARGS((FILE *ifp, int channel)); -static TabEntry * read_table ARGS((FILE *ifp, int tablen)); -static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)), int ochan )); -static void image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval, int channel)); -static void rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen)); -#define WORSTCOMPR(x) (2*(x) + 2) +static void +parseCommandLine(int argc, const char ** argv, + struct CmdlineInfo * const cmdlineP) { +/*---------------------------------------------------------------------------- + parse program command line described in Unix standard form by argc + and argv. Return the information in the options as *cmdlineP. + + If command line is internally inconsistent (invalid options, etc.), + issue error message to stderr and abort program. + + Note that the strings we return are stored in the storage that + was passed to us as the argv array. We also trash *argv. +-----------------------------------------------------------------------------*/ + optEntry * option_def; + /* Instructions to pm_optParseOptions3 on how to parse our options. + */ + optStruct3 opt; + + unsigned int option_def_index; + + MALLOCARRAY_NOFAIL(option_def, 100); + + option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "channel", OPT_UINT, + &cmdlineP->channel, + &cmdlineP->channelSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); + OPTENT3(0, "noverbose", OPT_FLAG, NULL, + NULL, 0); /* backward compatibility */ + + opt.opt_table = option_def; + opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ + opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ + + pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0); + /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + + free(option_def); + + if (!cmdlineP->channelSpec) + cmdlineP->channel = MAX_ZSIZE + 1; + /* Invalid value; to suppress Valgrind error */ + + if (argc-1 < 1) + cmdlineP->inputFileName = "-"; + else if (argc-1 == 1) + cmdlineP->inputFileName = argv[1]; + else + pm_error("Program takes at most one argument: input file name"); +} -static short verbose = 0; +/* basic I/O functions, taken from ilbmtoppm.c */ -int -main(argc, argv) - int argc; - char *argv[]; -{ - FILE *ifp; - int argn; - const char * const usage = "[-verbose] [-channel n] [sgifile]"; - TabEntry *table = NULL; - ScanLine *image; - Header *head; - pixval maxval; - int channel = -1; - - pnm_init(&argc, argv); - - argn = 1; - while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { - if( pm_keymatch(argv[argn], "-verbose", 2) ) - verbose++; - else - if( pm_keymatch(argv[argn], "-noverbose", 4) ) - verbose = 0; - else - if( pm_keymatch(argv[argn], "-channel", 2) ) { - char *s; - - ++argn; - if( argn >= argc ) - pm_usage(usage); - - s = argv[argn]; - channel = strtol(argv[argn], &s, 10); - if( s == argv[argn] || channel < 0) - pm_usage(usage); - } - else - pm_usage(usage); - ++argn; - } +static void +readerr(FILE * const f) { - if( argn < argc ) { - ifp = pm_openr( argv[argn] ); - argn++; - } + if (ferror(f)) + pm_error("read error"); else - ifp = stdin; + pm_error("premature EOF"); +} - if( argn != argc ) - pm_usage(usage); - head = read_header(ifp, channel); - maxval = head->pixmax - head->pixmin; - if( maxval > PNM_OVERALLMAXVAL ) - pm_error("Maximum sample value in input image (%d) is too large. " - "This program's limit is %d.", - maxval, PNM_OVERALLMAXVAL); - if (channel >= head->zsize) - pm_error("channel out of range - only %d channels in image", - head->zsize); - if( head->storage != STORAGE_VERBATIM ) - table = read_table(ifp, head->ysize * head->zsize); - if( head->bpc == 1 ) - image = read_channels(ifp, head, table, get_byte_as_short, channel); - else - image = read_channels(ifp, head, table, get_big_short, channel); - image_to_pnm(head, image, (xelval)maxval, channel); - pm_close(ifp); +static short +getBigShort(FILE * const ifP) { + + short s; + + if (pm_readbigshort(ifP, &s) == -1) + readerr(ifP); + + return s; +} + + + +static long +getBigLong(FILE * const ifP) { + + long l; + + if (pm_readbiglong(ifP, &l) == -1) + readerr(ifP); - exit(0); + return l; } + +static unsigned char +getByte(FILE * const ifP) { + + int i; + + i = getc(ifP); + if (i == EOF) + readerr(ifP); + + return (unsigned char) i; +} + + + +static void +readBytes(FILE * const ifP, + int const n, + char * const buf) { + + int r; + + r = fread((void *)buf, 1, n, ifP); + + if (r != n) + readerr(ifP); +} + + + +static short +getByteAsShort(FILE * const ifP) { + + return (short)getByte(ifP); +} + + + +static const char * +compressionName(unsigned char const storageCode) { + + switch (storageCode) { + case STORAGE_VERBATIM: + return "none"; + case STORAGE_RLE: + return "RLE"; + default: + return "unknown"; + } +} + + + +/* entry in RLE offset table */ +typedef struct { + long start; /* offset in file */ + long length; /* length of compressed scanline */ +} TabEntry; + +typedef short ScanElem; +typedef ScanElem * ScanLine; + +#define WORSTCOMPR(x) (2*(x) + 2) + + + static Header * -read_header(ifp, channel) - FILE *ifp; - int channel; -{ - Header *head; - - MALLOCVAR_NOFAIL(head); - - head->magic = get_big_short(ifp); - head->storage = get_byte(ifp); - head->bpc = get_byte(ifp); - head->dimension = get_big_short(ifp); - head->xsize = get_big_short(ifp); - head->ysize = get_big_short(ifp); - head->zsize = get_big_short(ifp); - head->pixmin = get_big_long(ifp); - head->pixmax = get_big_long(ifp); - read_bytes(ifp, 4, head->dummy1); - read_bytes(ifp, 80, head->name); - head->colormap = get_big_long(ifp); - read_bytes(ifp, 404, head->dummy2); - - if( head->magic != SGI_MAGIC ) +readHeader(FILE * const ifP, + bool const outChannelSpec, + bool const verbose) { + + Header * headP; + + MALLOCVAR_NOFAIL(headP); + + headP->magic = getBigShort(ifP); + headP->storage = getByte(ifP); + headP->bpc = getByte(ifP); + headP->dimension = getBigShort(ifP); + headP->xsize = getBigShort(ifP); + headP->ysize = getBigShort(ifP); + headP->zsize = getBigShort(ifP); + if (headP->zsize > MAX_ZSIZE) + pm_error("Too many channels in input image: %u", + (unsigned int) headP->zsize ); + headP->pixmin = getBigLong(ifP); + headP->pixmax = getBigLong(ifP); + if (headP->pixmin >= headP->pixmax) + pm_error("Invalid sgi image header: pixmin larger than pixmax"); + readBytes(ifP, 4, headP->dummy1); + readBytes(ifP, 80, headP->name); + headP->colormap = getBigLong(ifP); + readBytes(ifP, 404, headP->dummy2); + + if (headP->magic != SGI_MAGIC) pm_error("bad magic number - not an SGI image"); - if( head->storage != STORAGE_VERBATIM && head->storage != STORAGE_RLE ) + if (headP->storage != STORAGE_VERBATIM && headP->storage != STORAGE_RLE) pm_error("unknown compression type"); - if( head->bpc < 1 || head->bpc > 2 ) - pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc ); - if( head->colormap != CMAP_NORMAL ) + if (headP->bpc < 1 || headP->bpc > 2) + pm_error("illegal precision value %d (only 1-2 allowed)", headP->bpc); + if (headP->colormap != CMAP_NORMAL) pm_error("non-normal pixel data of a form we don't recognize"); /* adjust ysize/zsize to dimension, just to be sure */ - switch( head->dimension ) { + switch (headP->dimension) { + case 1: + headP->ysize = 1; + break; + case 2: + headP->zsize = 1; + break; + case 3: + switch (headP->zsize) { case 1: - head->ysize = 1; + headP->dimension = 2; break; case 2: - head->zsize = 1; + if (!outChannelSpec) + pm_message("2-channel image, using only first channel. " + "Extract alpha channel with -channel=1"); break; case 3: - switch( head->zsize ) { - case 1: - head->dimension = 2; - break; - case 2: - pm_error("don\'t know how to interpret 2-channel image"); - break; - case 3: - break; - default: - if (channel < 0) - pm_message("%d-channel image, using only first 3 channels", head->zsize); - break; - } break; default: - pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension); + if (!outChannelSpec) + pm_message("%u-channel image, using only first 3 channels " + "Extract %s with -channel=%c", + headP->zsize, + headP->zsize==4 ? + "alpha channel" : "additional channels", + headP->zsize==4 ? '3' : 'N'); + break; + } + break; + default: + pm_error("illegal dimension value %u (only 1-3 allowed)", + headP->dimension); } - if( verbose ) { - pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize); - pm_message("compression: %d = %s", head->storage, compression_name(head->storage)); - head->name[79] = '\0'; /* just to be safe */ - pm_message("Image name: \"%s\"", head->name); -#ifdef DEBUG - pm_message("bpc: %d dimension: %d zsize: %d", head->bpc, head->dimension, head->zsize); - pm_message("pixmin: %ld pixmax: %ld colormap: %ld", head->pixmin, head->pixmax, head->colormap); -#endif + if (verbose) { + pm_message("raster size %ux%u, %u channels", + headP->xsize, headP->ysize, headP->zsize); + pm_message("compression: 0x%02x = %s", + headP->storage, compressionName(headP->storage)); + headP->name[79] = '\0'; /* just to be safe */ + pm_message("Image name: '%s'", headP->name); } - return head; + return headP; } + static TabEntry * -read_table(ifp, tablen) - FILE *ifp; - int tablen; -{ - TabEntry *table; - int i; +readTable(FILE * const ifP, + int const tablen) { - MALLOCARRAY_NOFAIL(table, tablen); + TabEntry * table; + unsigned int i; -#ifdef DEBUG - pm_message("reading offset table"); -#endif + MALLOCARRAY_NOFAIL(table, tablen); - for( i = 0; i < tablen; i++ ) - table[i].start = get_big_long(ifp); - for( i = 0; i < tablen; i++ ) - table[i].length = get_big_long(ifp); + for (i = 0; i < tablen; ++i) + table[i].start = getBigLong(ifP); + for (i = 0; i < tablen; ++i) + table[i].length = getBigLong(ifP); return table; } +static void +rleDecompress(ScanElem * const srcStart, + int const srcleftStart, + ScanElem * const destStart, + int const destleftStart) { + + ScanElem * src; + int srcleft; + ScanElem * dest; + int destleft; + + for (src = srcStart, + srcleft = srcleftStart, + dest = destStart, + destleft = destleftStart; srcleft; ) { + + unsigned char const el = (unsigned char)(*src++ & 0xff); + unsigned int const count = (unsigned int)(el & 0x7f); + + --srcleft; + + if (count == 0) + return; + if (destleft < count) + pm_error("RLE error: too much input data " + "(space left %d, need %d)", destleft, count); + destleft -= count; + if (el & 0x80) { + unsigned int i; + if (srcleft < count) + pm_error("RLE error: not enough data for literal run " + "(data left %d, need %d)", srcleft, count); + srcleft -= count; + for (i = 0; i < count; ++i) + *dest++ = *src++; + } else { + unsigned int i; + if (srcleft == 0) + pm_error("RLE error: not enough data for replicate run"); + for (i = 0; i < count; ++i) + *dest++ = *src; + ++src; + --srcleft; + } + } + pm_error("RLE error: no terminating 0-byte"); +} + + + static ScanLine * -read_channels(ifp, head, table, func, ochan) - FILE *ifp; - Header *head; - TabEntry *table; - short (*func) ARGS((FILE *)); - int ochan; -{ - ScanLine *image; - ScanElem *temp; - int channel, maxchannel, row, sgi_index, i; - long offset, length; - -#ifdef DEBUG - pm_message("reading channels"); -#endif - - if (ochan < 0) { - maxchannel = (head->zsize < 3) ? head->zsize : 3; - MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel); - } else { - maxchannel = ochan + 1; +readChannels(FILE * const ifP, + Header * const head, + TabEntry * const table, + bool const outChannelSpec, + unsigned int const outChannel) { + + ScanLine * image; + ScanElem * temp; + unsigned int channel; + unsigned int maxchannel; + + if (outChannelSpec) { + maxchannel = outChannel + 1; + MALLOCARRAY_NOFAIL(image, head->ysize); + } else if (head->zsize <= 2) { + maxchannel = 1; MALLOCARRAY_NOFAIL(image, head->ysize); + } else { + maxchannel = 3; + MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel); } - if ( table ) + if (table) MALLOCARRAY_NOFAIL(temp, WORSTCOMPR(head->xsize)); - for( channel = 0; channel < maxchannel; channel++ ) { -#ifdef DEBUG - pm_message(" channel %d", channel); -#endif - for( row = 0; row < head->ysize; row++ ) { - int iindex; + for (channel = 0; channel < maxchannel; ++channel) { + unsigned int row; + for (row = 0; row < head->ysize; ++row) { + int const sgiIndex = channel * head->ysize + row; + + unsigned long int iindex; - sgi_index = channel * head->ysize + row; - iindex = (ochan < 0) ? sgi_index : row; - if (ochan < 0 || ochan == channel) + iindex = outChannelSpec ? row : sgiIndex; + if (!outChannelSpec || outChannel == channel) MALLOCARRAY_NOFAIL(image[iindex], head->xsize); - if( table ) { - if (channel < ochan) - continue; - - offset = table[sgi_index].start; - length = table[sgi_index].length; - if( head->bpc == 2 ) - length /= 2; - /* doc says length is in bytes, we are reading words */ - if( fseek(ifp, offset, SEEK_SET) != 0 ) - pm_error("seek error for offset %ld", offset); - - for( i = 0; i < length; i++ ) - temp[i] = (*func)(ifp); - rle_decompress(temp, length, image[iindex], head->xsize); - } - else { - for( i = 0; i < head->xsize; i++ ) - image[iindex][i] = (*func)(ifp); + if (table) { + if (!outChannelSpec || channel >= outChannel) { + pm_filepos const offset = (pm_filepos) + table[sgiIndex].start; + long const length = head->bpc == 2 ? + table[sgiIndex].length / 2 : + table[sgiIndex].length; + + unsigned int i; + + /* Note: (offset < currentPosition) can happen */ + + pm_seek2(ifP, &offset, sizeof(offset)); + + for (i = 0; i < length; ++i) + if (head->bpc == 1) + temp[i] = getByteAsShort(ifP); + else + temp[i] = getBigShort(ifP); + rleDecompress(temp, length, image[iindex], head->xsize); + } + } else { + unsigned int i; + for (i = 0; i < head->xsize; ++i) { + ScanElem p; + if (head->bpc == 1) + p = getByteAsShort(ifP); + else + p = getBigShort(ifP); + + if (!outChannelSpec || outChannel == channel) + image[iindex][i] = p; + } } } } - - if( table ) free(temp); + if (table) + free(temp); return image; } + static void -image_to_pnm(Header *head, ScanLine *image, xelval maxval, int channel) -{ - int col, row, format; - xel *pnmrow = pnm_allocrow(head->xsize); - int sub = head->pixmin; +imageToPnm(Header * const head, + ScanLine * const image, + xelval const maxval, + bool const outChannelSpec, + unsigned int const outChannel) { + + int const sub = head->pixmin; + xel * const pnmrow = pnm_allocrow(head->xsize); + + int row; + int format; - if( head->zsize == 1 || channel >= 0) { + if (head->zsize <= 2 || outChannelSpec) { pm_message("writing PGM image"); format = PGM_TYPE; - } - else { + } else { pm_message("writing PPM image"); format = PPM_TYPE; } - pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0); - for( row = head->ysize-1; row >= 0; row-- ) { - for( col = 0; col < head->xsize; col++ ) { - if( format == PGM_TYPE ) + pnm_writepnminit(stdout, head->xsize, head->ysize, maxval, format, 0); + for (row = head->ysize-1; row >= 0; --row) { + unsigned int col; + for (col = 0; col < head->xsize; ++col) { + if (format == PGM_TYPE) PNM_ASSIGN1(pnmrow[col], image[row][col] - sub); else { pixval r, g, b; @@ -329,135 +471,54 @@ image_to_pnm(Header *head, ScanLine *image, xelval maxval, int channel) PPM_ASSIGN(pnmrow[col], r, g, b); } } - pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0); + pnm_writepnmrow(stdout, pnmrow, head->xsize, maxval, format, 0); } pnm_freerow(pnmrow); } -static void -rle_decompress(src, srcleft, dest, destleft) - ScanElem *src; - int srcleft; - ScanElem *dest; - int destleft; -{ - int count; - unsigned char el; - - while( srcleft ) { - el = (unsigned char)(*src++ & 0xff); - --srcleft; - count = (int)(el & 0x7f); - if( count == 0 ) - return; - if( destleft < count ) - pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count); - destleft -= count; - if( el & 0x80 ) { - if( srcleft < count ) - pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count); - srcleft -= count; - while( count-- ) - *dest++ = *src++; - } - else { - if( srcleft == 0 ) - pm_error("RLE error: not enough data for replicate run"); - while( count-- ) - *dest++ = *src; - ++src; - --srcleft; - } - } - pm_error("RLE error: no terminating 0-byte"); -} - - -/* basic I/O functions, taken from ilbmtoppm.c */ - -static short -get_big_short(ifp) - FILE *ifp; -{ - short s; - - if( pm_readbigshort(ifp, &s) == -1 ) - readerr(ifp); - - return s; -} +int +main(int argc, const char * argv[]) { -static long -get_big_long(ifp) - FILE *ifp; -{ - long l; + struct CmdlineInfo cmdline; + FILE * ifP; + TabEntry * table; + ScanLine * image; + Header * headP; + xelval maxval; - if( pm_readbiglong(ifp, &l) == -1 ) - readerr(ifp); + pm_proginit(&argc, argv); - return l; -} + parseCommandLine(argc, argv, &cmdline); -static unsigned char -get_byte(ifp) - FILE* ifp; -{ - int i; + ifP = pm_openr_seekable(cmdline.inputFileName); - i = getc(ifp); - if( i == EOF ) - readerr(ifp); + headP = readHeader(ifP, cmdline.channelSpec, cmdline.verbose); - return (unsigned char) i; -} + maxval = headP->pixmax - headP->pixmin; + if (maxval > PNM_OVERALLMAXVAL) + pm_error("Maximum sample value in input image (%d) is too large. " + "This program's limit is %d.", + maxval, PNM_OVERALLMAXVAL); + if (cmdline.channelSpec && cmdline.channel >= headP->zsize) + pm_error("channel out of range - only %d channels in image", + headP->zsize); -static void -readerr(f) - FILE *f; -{ - if( ferror(f) ) - pm_error("read error"); + if (headP->storage != STORAGE_VERBATIM) + table = readTable(ifP, headP->ysize * headP->zsize); else - pm_error("premature EOF"); -} - + table = NULL; -static void -read_bytes(ifp, n, buf) - FILE *ifp; - int n; - char *buf; -{ - int r; + image = readChannels(ifP, headP, table, + cmdline.channelSpec, cmdline.channel); - r = fread((void *)buf, 1, n, ifp); - if( r != n ) - readerr(ifp); -} + imageToPnm(headP, image, maxval, cmdline.channelSpec, cmdline.channel); + pm_close(ifP); -static short -get_byte_as_short(ifp) - FILE *ifp; -{ - return (short)get_byte(ifp); + return 0; } -static const char * -compression_name(char compr) -{ - switch( compr ) { - case STORAGE_VERBATIM: - return "none"; - case STORAGE_RLE: - return "RLE"; - default: - return "unknown"; - } -} - |