/* ximtoppm.c - read an Xim file and produce a portable pixmap ** ** Copyright (C) 1991 by Jef Poskanzer. ** ** 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 ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #define _BSD_SOURCE 1 /* This makes sure strdup() is in string.h */ #include #include "ppm.h" #include "xim.h" #include "shhopt.h" struct cmdline_info { /* All the information the user supplied in the command line, in a form easy for the program to use. */ char *input_filename; char *alpha_filename; int alpha_stdout; } cmdline; static void parse_command_line(int argc, char ** argv, struct cmdline_info *cmdline_p); static int ReadXim ARGS(( FILE *in_fp, XimImage *xim )); static int ReadXimHeader ARGS(( FILE *in_fp, XimImage *header )); static int ReadXimImage ARGS(( FILE *in_fp, XimImage *xim )); static int ReadImageChannel ARGS(( FILE *infp, byte *buf, unsigned int *bufsize, int encoded )); int main( argc, argv ) int argc; char *argv[]; { FILE *ifp, *imageout_file, *alpha_file; XimImage xim; pixel *pixelrow, colormap[256]; gray *alpharow; /* The alpha channel of the row we're currently converting, in pgm fmt*/ register pixel *pP; gray *aP; int rows, cols, row, mapped; register int col; pixval maxval; ppm_init( &argc, argv ); parse_command_line(argc, argv, &cmdline); if ( cmdline.input_filename != NULL ) ifp = pm_openr( cmdline.input_filename ); else ifp = stdin; if (cmdline.alpha_stdout) alpha_file = stdout; else if (cmdline.alpha_filename == NULL) alpha_file = NULL; else { alpha_file = pm_openw(cmdline.alpha_filename); } if (cmdline.alpha_stdout) imageout_file = NULL; else imageout_file = stdout; if ( ! ReadXim( ifp, &xim ) ) pm_error( "can't read Xim file" ); rows = xim.height; cols = xim.width; if ( xim.nchannels == 1 && xim.bits_channel == 8 ) { int i; mapped = 1; maxval = 255; for ( i = 0; i < xim.ncolors; ++i ) { PPM_ASSIGN( colormap[i], xim.colors[i].red, xim.colors[i].grn, xim.colors[i].blu ); /* Should be colormap[xim.colors[i].pixel], but Xim is broken. */ } } else if ( xim.nchannels == 3 || xim.nchannels == 4 ) { mapped = 0; maxval = pm_bitstomaxval( xim.bits_channel ); } else pm_error( "unknown Xim file type, nchannels == %d, bits_channel == %d", xim.nchannels, xim.bits_channel ); if (imageout_file) ppm_writeppminit( imageout_file, cols, rows, maxval, 0 ); if (alpha_file) pgm_writepgminit( alpha_file, cols, rows, maxval, 0 ); pixelrow = ppm_allocrow( cols ); alpharow = pgm_allocrow( cols ); for ( row = 0; row < rows; ++row ) { if ( mapped ) { register byte *xbp; for ( col = 0, pP = pixelrow, xbp = xim.data + row * xim.bytes_per_line; col < cols; ++col, ++pP, ++xbp ) *pP = colormap[*xbp]; *aP = 0; } else { register byte *xrbp, *xgbp, *xbbp; for ( col = 0, pP = pixelrow, aP = alpharow, xrbp = xim.data + row * xim.bytes_per_line, xgbp = xim.grn_data + row * xim.bytes_per_line, xbbp = xim.blu_data + row * xim.bytes_per_line; col < cols; ++col, ++pP, ++xrbp, ++xgbp, ++xbbp ) { PPM_ASSIGN( *pP, *xrbp, *xgbp, *xbbp ); if (xim.nchannels > 3) *aP = *(xim.other + row * xim.bytes_per_line + col); else *aP = 0; } } if (imageout_file) ppm_writeppmrow( imageout_file, pixelrow, cols, maxval, 0 ); if (alpha_file) pgm_writepgmrow( alpha_file, alpharow, cols, maxval, 0 ); } pm_close( ifp ); if (imageout_file) pm_close( imageout_file ); if (alpha_file) pm_close( alpha_file ); exit( 0 ); } /* The rest is excerpted and slightly modified from the X.V11R4 version ** of xim_io.c. */ /*********************************************************************** * File: xlib.c * Author: Philip Thompson * $Date: 89/11/01 10:14:23 $ * $Revision: 1.14 $ * Purpose: General xim libray of utililities * Copyright (c) 1988 Philip R. Thompson * Computer Resource Laboratory (CRL) * Dept. of Architecture and Planning * M.I.T., Rm 9-526 * Cambridge, MA 02139 * This software and its documentation may be used, copied, modified, * and distributed for any purpose without fee, provided: * -- The above copyright notice appears in all copies. * -- This disclaimer appears in all source code copies. * -- The names of M.I.T. and the CRL are not used in advertising * or publicity pertaining to distribution of the software * without prior specific written permission from me or CRL. * I provide this software freely as a public service. It is NOT a * commercial product, and therefore is not subject to an an implied * warranty of merchantability or fitness for a particular purpose. I * provide it as is, without warranty. * This software is furnished only on the basis that any party who * receives it indemnifies and holds harmless the parties who furnish * it against any claims, demands, or liabilities connected with using * it, furnishing it to others, or providing it to a third party. * * Philip R. Thompson (phils@athena.mit.edu) ***********************************************************************/ static int ReadXim(in_fp, xim) FILE *in_fp; XimImage *xim; { if (!ReadXimHeader(in_fp, xim)) { pm_message("can't read xim header" ); return(0); } if (!ReadXimImage(in_fp, xim)) { pm_message("can't read xim data" ); return(0); } return(1); } static int ReadXimHeader(in_fp, header) FILE *in_fp; XimImage *header; { int i; char *cp; XimAsciiHeader a_head; cp = (char *) header; for (i = 0; i < sizeof(XimImage); ++i ) *cp++ = 0; /* Read header and verify image file formats */ if (fread((char *)&a_head, sizeof(ImageHeader), 1, in_fp) != 1) { pm_message("ReadXimHeader: unable to read file header" ); return(0); } if (atoi(a_head.header_size) != sizeof(ImageHeader)) { pm_message("ReadXimHeader: header size mismatch" ); return(0); } if (atoi(a_head.file_version) != IMAGE_VERSION) { pm_message("ReadXimHeader: incorrect Image_file version" ); return(0); } header->width = atoi(a_head.image_width); header->height = atoi(a_head.image_height); header->ncolors = atoi(a_head.num_colors); header->nchannels = atoi(a_head.num_channels); header->bytes_per_line = atoi(a_head.bytes_per_line); /* header->npics = atoi(a_head.num_pictures); */ header->bits_channel = atoi(a_head.bits_per_channel); header->alpha_flag = atoi(a_head.alpha_channel); if (strlen(a_head.author)) { if (!(header->author = calloc((unsigned int)strlen(a_head.author)+1, 1))) { pm_message("ReadXimHeader: can't calloc author string" ); return(0); } header->width = atoi(a_head.image_width); strncpy(header->author, a_head.author, strlen(a_head.author)); } if (strlen(a_head.date)) { if (!(header->date =calloc((unsigned int)strlen(a_head.date)+1,1))){ pm_message("ReadXimHeader: can't calloc date string" ); return(0); } header->width = atoi(a_head.image_width); strncpy(header->date, a_head.date, strlen(a_head.date)); } if (strlen(a_head.program)) { if (!(header->program = calloc( (unsigned int)strlen(a_head.program) + 1, 1))) { pm_message("ReadXimHeader: can't calloc program string" ); return(0); } header->width = atoi(a_head.image_width); strncpy(header->program, a_head.program,strlen(a_head.program)); } /* Do double checking for bakwards compatibility */ if (header->npics == 0) header->npics = 1; if (header->bits_channel == 0) header->bits_channel = 8; else if (header->bits_channel == 24) { header->nchannels = 3; header->bits_channel = 8; } if ((int)header->bytes_per_line == 0) header->bytes_per_line = (header->bits_channel == 1 && header->nchannels == 1) ? (header->width + 7) / 8 : header->width; header->datasize =(unsigned int)header->bytes_per_line * header->height; if (header->nchannels == 3 && header->bits_channel == 8) header->ncolors = 0; else if (header->nchannels == 1 && header->bits_channel == 8) { header->colors = (Color *)calloc((unsigned int)header->ncolors, sizeof(Color)); if (header->colors == NULL) { pm_message("ReadXimHeader: can't calloc colors" ); return(0); } for (i=0; i < header->ncolors; i++) { header->colors[i].red = a_head.c_map[i][0]; header->colors[i].grn = a_head.c_map[i][1]; header->colors[i].blu = a_head.c_map[i][2]; } } return(1); } static int ReadXimImage(in_fp, xim) FILE *in_fp; XimImage *xim; { if (xim->data) { free((char *)xim->data); xim->data = (byte *)0; } if (xim->grn_data) { free((char *)xim->grn_data); xim->grn_data = (byte *)0; } if (xim->blu_data) { free((char *)xim->blu_data); xim->blu_data = (byte *)0; } if (xim->other) { free((char *)xim->other); xim->other = (byte *)0; } xim->npics = 0; if (!(xim->data = (byte *)calloc(xim->datasize, 1))) { pm_message("ReadXimImage: can't malloc pixmap data" ); return(0); } if (!ReadImageChannel(in_fp, xim->data, &xim->datasize, 0)) { pm_message("ReadXimImage: end of the images" ); return(0); } if (xim->nchannels == 3) { xim->grn_data = (byte *)malloc(xim->datasize); xim->blu_data = (byte *)malloc(xim->datasize); if (xim->grn_data == NULL || xim->blu_data == NULL) { pm_message("ReadXimImage: can't malloc rgb channel data" ); free((char *)xim->data); if (xim->grn_data) free((char *)xim->grn_data); if (xim->blu_data) free((char *)xim->blu_data); xim->data = xim->grn_data = xim->blu_data = (byte*)0; return(0); } if (!ReadImageChannel(in_fp, xim->grn_data, &xim->datasize, 0)) return(0); if (!ReadImageChannel(in_fp, xim->blu_data, &xim->datasize, 0)) return(0); } if (xim->nchannels > 3) { /* In theory, this can be any fourth channel, but the only one we know about is an Alpha channel, so we'll call it that, even though we process it generically. */ if ((xim->other = (byte *)malloc(xim->datasize)) == NULL) { pm_message("ReadXimImage: can't malloc alpha data" ); return(0); } if (!ReadImageChannel(in_fp, xim->other, &xim->datasize, 0)) return(0); } xim->npics = 1; return(1); } static int ReadImageChannel(infp, buf, bufsize, encoded) FILE *infp; byte *buf; unsigned int *bufsize; int encoded; { register int i, runlen, nbytes; register unsigned int j; register byte *line; long marker; if (!encoded) j = fread((char *)buf, 1, (int)*bufsize, infp); else { if ((line=(byte *)malloc((unsigned int)BUFSIZ)) == NULL) { pm_message("ReadImageChannel: can't malloc() fread string" ); return(0); } /* Unrunlength encode data */ marker = ftell(infp); j = 0; while (((nbytes=fread((char *)line, 1, BUFSIZ, infp)) > 0) && (j < *bufsize)) { for (i=0; (i < nbytes) && (j < *bufsize); i++) { runlen = (int)line[i]+1; i++; while (runlen--) buf[j++] = line[i]; } marker += i; } /* return to the begining of the next image's bufffer */ if (fseek(infp, marker, 0) == -1) { pm_message("ReadImageChannel: can't fseek to location in image buffer" ); return(0); } free((char *)line); } if (j != *bufsize) { pm_message("unable to complete channel: %u / %u (%d%%)", j, *bufsize, (int)(j*100.0 / *bufsize) ); *bufsize = j; } return(1); } static void parse_command_line(int argc, char ** argv, struct cmdline_info *cmdline_p) { /*---------------------------------------------------------------------------- Note that many of the strings that this function returns in the *cmdline_p structure are actually in the supplied argv array. And sometimes, one of these strings is actually just a suffix of an entry in argv! -----------------------------------------------------------------------------*/ optStruct *option_def = malloc(100*sizeof(optStruct)); /* Instructions to OptParseOptions on how to parse our options. */ unsigned int option_def_index; option_def_index = 0; /* incremented by OPTENTRY */ OPTENTRY(0, "alphaout", OPT_STRING, &cmdline_p->alpha_filename, 0); /* Set the defaults */ cmdline_p->alpha_filename = NULL; optParseOptions(&argc, argv, option_def, 0); /* Uses and sets argc, argv, and all of *cmdline_p. */ if (argc - 1 == 0) cmdline_p->input_filename = NULL; /* he wants stdin */ else if (argc - 1 == 1) cmdline_p->input_filename = strdup(argv[1]); else pm_error("Too many arguments. The only argument accepted\n" "is the input file specificaton"); if (cmdline_p->alpha_filename && strcmp(cmdline_p->alpha_filename, "-") == 0 ) cmdline_p->alpha_stdout = 1; else cmdline_p->alpha_stdout = 0; }