/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA, or visit http://www.gnu.org/copyleft/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmppm.c,v 1.7 2005/02/19 16:10:45 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.7 $ * $Log: rmppm.c,v $ * Revision 1.7 2005/02/19 16:10:45 wes * Distro sync and consolidation. * * Revision 1.6 2005/01/23 17:09:39 wes * Copyright update to 2005. * * Revision 1.5 2004/03/18 15:51:16 wes * Minor changes to switch statements to avoid compile warning on SGI * about "statement not reached" when there is a return inside a case. * * Revision 1.4 2004/01/17 04:09:05 wes * Updated copyright line for 2004. * * Revision 1.3 2003/06/20 01:38:23 wes * Minor documentation tweaks. * * Revision 1.2 2003/02/02 02:07:21 wes * Updated copyright to 2003. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.3 2003/01/16 22:21:19 wes * Updated all source files to reflect new organization of header files: * all header files formerly located in include/rmaux, include/rmi, include/rmv * are now located in include/rm. * * Revision 1.2 2002/06/02 15:18:45 wes * Fixed minor indexing bug. * * Revision 1.1 2001/06/03 20:18:15 wes * Initial entry (v140-beta1) * */ /* * file: rmppm.c * * PGM/PPM input/output * * OpenRM 1.4.0-alpha2 * * 02/23/01 jdb */ #include #include /*#define DEBUG*/ /* * ---------------------------------------------------- * @Name rmiReadPPM @pstart RMimage * rmiReadPPM (const char * filename, RMenum format, unsigned char alpha) @pend @astart char * filename - filename of PGM/PPM image file to read RMenum format - image format: RM_IMAGE_ALPHA, RM_IMAGE_LUMINANCE, RM_IMAGE_RGB, RM_IMAGE_RGBA unsigned char alpha - alpha for RMimage [0..255] @aend @dstart Read in a PGM/PPM file and return a handle to the RMimage object created from the image file. Certain simplifying assumptions are made about the file format:
    image header
       - the magic number 
       - comments (lines starting with "#") may follow 
       - the width and height follow, delineated by whitespace 
       - comments may follow 
       - maximum value parameter follows, delineated by whitespace 
       
       P3
       # this is an example 128x128 PPM file (8-bit RGB pixels)
       128 128
       255
       [image data]

    image pixels
       - ASCII lines may be no more than 70 characters (PBM/PGM/PPM spec)
       - image data is assumed to be ordered top to bottom, left to right
 
The following RMimage formats are supported: RM_IMAGE_ALPHA, RM_IMAGE_LUMINANCE, RM_IMAGE_RGB, RM_IMAGE_RGBA The user-specified image format controls how the image is read in as an RMimage:
    PGM files: map directly to RM_IMAGE_ALPHA + RM_IMAGE_LUMINANCE
               channel replicated RGB to RM_IMAGE_RGB 
               channel replicated RGB to RM_IMAGE_RGBA with alpha

    PPM files: image luminance to RM_IMAGE_ALPHA + RM_IMAGE_LUMINANCE
               map directly to RM_IMAGE_RGB
	       map directly to RM_IMAGE_RGBA with alpha
 
Currently, pixels must have type RM_UNSIGNED_BYTE. @dend * ---------------------------------------------------- */ RMimage * rmiReadPPM (const char * filename, RMenum format, unsigned char alpha) { int i, type, width, height, max; char buffer[80]; FILE * fp; unsigned char * pixels = NULL; RMimage * image = NULL; /* verify legal format for rmiReadPPM */ if ((format == RM_IMAGE_ALPHA) || (format == RM_IMAGE_LUMINANCE) || (format == RM_IMAGE_RGB) || (format == RM_IMAGE_RGBA)) { /* open input file */ if ((fp = fopen (filename, "r")) != NULL) { /* get magic number */ fgets (buffer, 70, fp); sscanf (buffer, "P%d", &type); /* get width & height */ do { fgets (buffer, 70, fp); } while (buffer[0] == '#'); sscanf (buffer, "%d %d", &width, &height); /* get component maximum value (RM_UNSIGNED_BYTE) */ do { fgets (buffer, 70, fp); } while (buffer[0] == '#'); sscanf (buffer, "%d", &max); #ifdef DEBUG printf ("reading image file: %s\n", filename); printf ("type = %d\n", type); printf ("width = %d\nheight = %d\n", width, height); printf ("max = %d\n", max); #endif /* read in pixels */ switch (type) { case RMI_PPM_P2: /* ASCII greyscale image */ { switch (format) { case RM_IMAGE_ALPHA: /* 1 component per pixel */ case RM_IMAGE_LUMINANCE: { unsigned int cmp; int count = width * height; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i++) { fscanf (fp, "%d", &cmp); *(pixels + i) = (unsigned char)cmp; } } break; case RM_IMAGE_RGB: /* 3 components per pixel */ { unsigned int cmp; int count = width * height * 3; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 3) { fscanf (fp, "%d", &cmp); *(pixels + i) = *(pixels + i + 1) = *(pixels + i + 2) = (unsigned char)cmp; } } break; case RM_IMAGE_RGBA: /* 4 components per pixel */ { unsigned int cmp; int count = width * height * 4; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 4) { fscanf (fp, "%d", &cmp); *(pixels + i) = *(pixels + i + 1) = *(pixels + i + 2) = (unsigned char)cmp; *(pixels + i + 3) = alpha; } } break; default: /* bogus image format */ break; } } break; case RMI_PPM_P3: /* ASCII RGB image */ { switch (format) { case RM_IMAGE_ALPHA: /* 1 component per pixel */ case RM_IMAGE_LUMINANCE: { int count = width * height; unsigned int color[3]; pixels = malloc (count * sizeof (unsigned char)); /* compute "standard" luminance image (RGB --> greyscale) */ for (i = 0; i < count; i++) { fscanf (fp, "%d %d %d", color, (color + 1), (color + 2)); *(pixels + i) = (unsigned char)((0.28 * color[0]) + (0.59 * color[1]) + (0.13 * color[2])); } } break; case RM_IMAGE_RGB: /* 3 components per pixel */ { unsigned int cmp; int count = width * height * 3; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 3) { fscanf (fp, "%d", &cmp); *(pixels + i) = (unsigned char)cmp; fscanf (fp, "%d", &cmp); *(pixels + i + 1) = (unsigned char)cmp; fscanf (fp, "%d", &cmp); *(pixels + i + 2) = (unsigned char)cmp; } } break; case RM_IMAGE_RGBA: /* 4 components per pixel */ { unsigned int cmp; int count = width * height * 4; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 4) { fscanf (fp, "%d", &cmp); *(pixels + i) = (unsigned char)cmp; fscanf (fp, "%d", &cmp); *(pixels + i + 1) = (unsigned char)cmp; fscanf (fp, "%d", &cmp); *(pixels + i + 2) = (unsigned char)cmp; *(pixels + i + 3) = alpha; } } break; default: /* bogus image format */ break; } } break; case RMI_PPM_P5: /* binary greyscale image */ { switch (format) { case RM_IMAGE_ALPHA: /* 1 component per pixel */ case RM_IMAGE_LUMINANCE: { int count = width * height; pixels = malloc (count * sizeof (unsigned char)); fread (pixels, 1, count, fp); } break; case RM_IMAGE_RGB: /* 3 components per pixel */ { int count = width * height * 3; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 3) { fread ((pixels + i), 1, 1, fp); *(pixels + i + 1) = *(pixels + i + 2) = *(pixels + i); } } break; case RM_IMAGE_RGBA: /* 4 components per pixel */ { int count = width * height * 4; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 4) { fread ((pixels + i), 1, 1, fp); *(pixels + i + 1) = *(pixels + i + 2) = *(pixels + i); *(pixels + i + 3) = alpha; } } break; default: /* bogus image format */ break; } } break; case RMI_PPM_P6: /* binary RGB image */ { switch (format) { case RM_IMAGE_ALPHA: /* 1 component per pixel */ case RM_IMAGE_LUMINANCE: { int count = width * height; unsigned char color[3]; pixels = malloc (count * sizeof (unsigned char)); /* compute "standard" luminance image (RGB --> greyscale) */ for (i = 0; i < count; i++) { fread (color, 1, 3, fp); *(pixels + i) = (unsigned char)((0.28 * color[0]) + (0.59 * color[1]) + (0.13 * color[2])); } } break; case RM_IMAGE_RGB: /* 3 components per pixel */ { int count = width * height * 3; pixels = malloc (count * sizeof (unsigned char)); fread (pixels, 1, count, fp); } break; case RM_IMAGE_RGBA: /* 4 components per pixel */ { int count = width * height * 4; pixels = malloc (count * sizeof (unsigned char)); for (i = 0; i < count; i += 4) { fread ((pixels + i), 1, 3, fp); *(pixels + 3) = alpha; } } break; default: /* bogus image format */ break; } } break; default: { sprintf (buffer, "rmiReadPPM () : bogus magic number in image header: %s\n", filename); rmError (buffer); fclose (fp); return (NULL); } } /* stuff pixels into image object, discard pixel store, close input file */ image = rmImageNew (2, width, height, 1, format, RM_UNSIGNED_BYTE, RM_COPY_DATA); rmImageSetPixelData (image, (void *)pixels, RM_COPY_DATA, NULL); free (pixels); fclose (fp); } else { sprintf (buffer, "rmiReadPPM () : cannot open file \"%s\" for input\n", filename); rmError (buffer); return (NULL); } } else { rmError ("rmiReadPPM () : invalid RMimage format\n"); return (NULL); } return (image); } /* * ---------------------------------------------------- * @Name rmiWritePPM @pstart RMenum rmiWritePPM (const char * filename, int type, const RMimage * image) @pend @astart const char * filename - filename for RMimage write int type - image file type (RMI_PPM_ASCII or RMI_PPM_BINARY) const RMimage * image - image to write @aend @dstart Write the user-supplied RMimage to a PGM/PPM image file. The type of file written is inferred from the RMimage format and user-supplied type. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The following RMimage formats are supported by PBM/PGM/PPM files:
    RM_IMAGE_ALPHA, RM_IMAGE_LUMINANCE (output as PGM) 
    RM_IMAGE_RGB (outputs as PPM)
    RM_IMAGE_RGBA (outputs as PPM, alpha discarded)
 
The file format follows the PGM/PPM spec, as described in the docs for rmiWritePPM (). Currently, pixel components must have type RM_UNSIGNED_BYTE. @dend * ---------------------------------------------------- */ RMenum rmiWritePPM (const char * filename, int type, const RMimage * image) { int i; char buffer[80]; FILE * fp; unsigned char * pixels; /* open output file */ if ((fp = fopen (filename, "w")) != NULL) { int dim, width, height, depth, comp; /* verify legal format for rmiWritePPM */ if ((rmImageGetType (image) == RM_UNSIGNED_BYTE) && (rmImageGetImageSize (image, &dim, &width, &height, &depth, &comp, NULL) == RM_CHILL) && (pixels = (unsigned char *)(rmImageGetPixelData (image))) != NULL) { #ifdef DEBUG printf ("writing image file: %s\n", filename); printf ("dim = %d\n", dim); printf ("width = %d\nheight = %d\n", width, height); printf ("comp = %d\n", comp); #endif switch (rmImageGetFormat (image)) { /* PGM (1 component per pixel) */ case RM_IMAGE_ALPHA: case RM_IMAGE_LUMINANCE: { /* write header */ switch (type) { case RMI_PPM_ASCII: { /* write header */ fprintf (fp, "P2\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ for (i = 0; i < (width * height); i++) { if ((!(i % 15)) && (i >= 15)) /* 5 image components per line */ fprintf (fp, "\n"); fprintf (fp, "%3d ", *(pixels + i)); } } break; case RMI_PPM_BINARY: { /* write header */ fprintf (fp, "P5\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ fwrite(pixels, 1, (width * height), fp); } break; default: { rmError ("rmiWritePPM () : bogus type for writing.\n"); } break; } } break; /* PPM (3 components per pixel) */ case RM_IMAGE_RGB: { switch (type) { case RMI_PPM_ASCII: { /* write header */ fprintf (fp, "P3\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ for (i = 0; i < (width * height * 3); i += 3) { if ((!(i % 15)) && (i >= 15)) /* 5 image components per line */ fprintf (fp, "\n"); fprintf (fp, "%3d %3d %3d ", *(pixels + i), *(pixels + i + 1), *(pixels + i + 2)); } } break; case RMI_PPM_BINARY: { /* write header */ fprintf (fp, "P6\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ fwrite (pixels, 1, (width * height * 3), fp); } break; default: { rmError ("rmiWritePPM () : bogus type for writing.\n"); } break; } } break; /* PPM (3 components per pixel, alpha is discarded) */ case RM_IMAGE_RGBA: { switch (type) { case RMI_PPM_ASCII: { /* write header */ fprintf (fp, "P3\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ for (i = 0; i < (width * height * 4); i += 4) { if ((!(i % 15)) && (i >= 15)) /* 5 image components per line */ fprintf (fp, "\n"); fprintf (fp, "%3d %3d %3d ", *(pixels + i), *(pixels + i + 1), *(pixels + i + 2)); } } break; case RMI_PPM_BINARY: { /* write header */ fprintf (fp, "P6\n"); fprintf (fp, "%d %d\n", width, height); fprintf (fp, "%d\n", 255); /* current component type is RM_UNSIGNED_BYTE */ /* write pixels */ for (i = 0; i < (width * height * 4); i += 4) { fwrite ((pixels + i), 1, 3, fp); } } break; default: { rmError ("rmiWritePPM () : bogus type for writing.\n"); } break; } } break; default: { rmError ("rmiWritePPM () : bogus RMimage format for writing.\n"); fclose (fp); return (RM_WHACKED); } } fclose (fp); } } else { sprintf (buffer, "rmiReadPPM () : cannot open file \"%s\" for output\n", filename); rmError (buffer); return (RM_WHACKED); } return (RM_CHILL); }