/* * 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: rmjpeg.c,v 1.9 2005/02/19 16:10:45 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.9 $ * $Log: rmjpeg.c,v $ * Revision 1.9 2005/02/19 16:10:45 wes * Distro sync and consolidation. * * Revision 1.8 2005/01/23 17:09:39 wes * Copyright update to 2005. * * Revision 1.7 2004/03/10 01:49:53 wes * Updated documentation on rmiWriteJPEG. * * Revision 1.6 2004/01/17 04:09:05 wes * Updated copyright line for 2004. * * Revision 1.5 2003/06/20 01:38:14 wes * Added code to rmiWriteJPEG that will permit only RMimages with * RM_IMAGE_RGB format pixels to be processed. All other formats will * produce an error message. Future work might be to accommodate a greater * breadth of RMimage pixel formats. * * Revision 1.4 2003/06/19 20:53:56 wes * Minor documentation tweaks. * * Revision 1.3 2003/02/14 00:21:20 wes * Minor code cleanups. * * 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.4 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.3 2002/06/02 15:18:45 wes * Fixed minor indexing bug. * * Revision 1.2 2001/06/04 01:00:05 wes * Added documentation for JPEG i/o routines. * * Revision 1.1 2001/06/03 20:18:15 wes * Initial entry (v140-beta1) * */ #include #include #include /* ugh */ #if RM_JPEG struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* for return to caller */ }; typedef struct my_error_mgr * my_error_ptr; #endif /* * Here's the routine that will replace the standard error_exit method: */ #if RM_JPEG METHODDEF(void) my_error_exit (j_common_ptr cinfo) { /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ my_error_ptr myerr = (my_error_ptr) cinfo->err; /* Always display the message. */ /* We could postpone this until after returning, if we chose. */ (*cinfo->err->output_message) (cinfo); /* Return control to the setjmp point */ longjmp(myerr->setjmp_buffer, 1); } #endif /* * ---------------------------------------------------- * @Name rmiWriteJPEG @pstart RMenum rmiWriteJPEG(const char *filename, int quality, const RMimage *toWrite) @pend @astart const char *filename - the name of the file to be written. int quality - integer value that controls degree of lossiness in JPEG file. The value of quality ranges from 0-100, with 100 being the least amount of loss, and smaller numbers producing increasing amounts of error. Smaller numbers result in smaller output file size. const RMimage *toWrite - the RMimage object that will be written to the file in JPEG format. @aend @dstart Writes a JPEG image raster file named "filename" using the input RMimage object "toWrite" as the source. The quality of the JPEG file is determined by the "quality" parameter, which ranges from 0-100. Smaller quality values produce greater amounts of compression, smaller files and poorer image image quality. Larger quality values produce larger files, less compression, and better image quality. Returns RM_CHILL upon success, or RM_WHACKED upon failure. This routine works only on RMimage objects that contain RGB images. @dend * ---------------------------------------------------- */ RMenum rmiWriteJPEG(const char *filename, int quality, const RMimage *toWrite) { #if RM_JPEG int image_width; int image_height; unsigned char * pixeldata; /* this code is more or less a cut-n-paste of the decompress file() routine in the example.c file shipped with the jpeg-6b source. comments have been left pretty much intact. */ struct jpeg_compress_struct cinfo; /* This struct represents a JPEG error handler. It is declared separately * because applications often want to supply a specialized error handler * (see the second half of this file for an example). But here we just * take the easy way out and use the standard error handler, which will * print a message on stderr and call exit() if compression fails. * Note that this struct must live as long as the main JPEG parameter * struct, to avoid dangling-pointer problems. */ struct jpeg_error_mgr jerr; /* More stuff */ FILE * outfile; /* target file */ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ int j; int bytesPerScanline; /* physical row width in image buffer */ if ((rmImageGetImageSize(toWrite, NULL, &image_width, &image_height, NULL, NULL, NULL) == RM_WHACKED) || ((pixeldata = (unsigned char *)rmImageGetPixelData(toWrite)) == NULL)) { rmError("rmiWriteJPEGFile() : error reading image dimensions or pixel data. No JPEG file is being produced. "); return(RM_WHACKED); } /* next, check for valid pixel format and type */ { RMenum pFormat, pType; pFormat = rmImageGetFormat (toWrite); pType = rmImageGetType (toWrite); if (pFormat != RM_IMAGE_RGB) { rmError("rmiWriteJPEG() error: the input RMimage does not have RM_IMAGE_RGB format pixels, unable to write the JPEG file. "); return RM_WHACKED; } if (pType != RM_UNSIGNED_BYTE) { rmError("rmiWriteJPEG() error: the input RMimage does not have RM_UNSIGNED_BYTE pixels, unable to write the JPEG file. "); return RM_WHACKED; } } /* Step 1: allocate and initialize JPEG compression object */ /* We have to set up the error handler first, in case the initialization * step fails. (Unlikely, but it could happen if you are out of memory.) * This routine fills in the contents of struct jerr, and returns jerr's * address which we place into the link field in cinfo. */ cinfo.err = jpeg_std_error(&jerr); /* Now we can initialize the JPEG compression object. */ jpeg_create_compress(&cinfo); /* Step 2: specify data destination (eg, a file) */ /* Note: steps 2 and 3 can be done in either order. */ /* Here we use the library-supplied code to send compressed data to a * stdio stream. You can also write your own code to do something else. * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that * requires it in order to write binary files. */ if ((outfile = fopen(filename, "wb")) == NULL) { char buf[1024]; sprintf(buf,"rmiWriteJPEGFile(): can't open output file %s. ",filename); rmError(buf); return(RM_WHACKED); } jpeg_stdio_dest(&cinfo, outfile); /* Step 3: set parameters for compression */ /* First we supply a description of the input image. * Four fields of the cinfo struct must be filled in: */ cinfo.image_width = image_width; /* image width and height, in pixels */ cinfo.image_height = image_height; cinfo.input_components = 3; /* # of color components per pixel */ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ /* Now use the library's routine to set default compression parameters. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) */ jpeg_set_defaults(&cinfo); /* Now you can set any non-default parameters you wish to. * Here we just illustrate the use of quality (quantization table) scaling: */ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); /* Step 4: Start compressor */ /* TRUE ensures that we will write a complete interchange-JPEG file. * Pass TRUE unless you are very sure of what you're doing. */ jpeg_start_compress(&cinfo, TRUE); /* Step 5: while (scan lines remain to be written) */ /* jpeg_write_scanlines(...); */ /* JSAMPLEs per row in image_buffer */ bytesPerScanline = rmImageGetBytesPerScanline(toWrite); for (j=0;jalloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); /* wes: config pixel buffer */ *widthReturn = cinfo.output_width; *heightReturn = cinfo.output_height; pd = (unsigned char *)malloc(sizeof(unsigned char)*cinfo.output_width*cinfo.output_height*cinfo.output_components); indx = 0; /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ /* Here we use the library's state variable cinfo.output_scanline as the * loop counter, so that we don't have to keep track ourselves. */ while (cinfo.output_scanline < cinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines(&cinfo, buffer, 1); /* Assume put_scanline_someplace wants a pointer and sample count. */ /* put_scanline_someplace(buffer[0], row_stride); */ memcpy(pd+indx, buffer[0], row_stride); indx += row_stride; } /* Step 7: Finish decompression */ (void) jpeg_finish_decompress(&cinfo); /* We can ignore the return value since suspension is not possible * with the stdio data source. */ /* Step 8: Release JPEG decompression object */ /* This is an important step since it will release a good deal of memory. */ jpeg_destroy_decompress(&cinfo); /* After finish_decompress, we can close the input file. * Here we postpone it until after no more JPEG errors are possible, * so as to simplify the setjmp error logic above. (Actually, I don't * think that jpeg_destroy can do an error exit, but why assume anything...) */ fclose(infile); /* At this point you may want to check to see whether any corrupt-data * warnings occurred (test whether jerr.pub.num_warnings is nonzero). */ *pixelDataReturn = pd; /* And we're done! */ return RM_CHILL; } #endif /* * ---------------------------------------------------- * @Name rmiReadJPEG @pstart RMimage *rmiReadJPEG (const char * filename) @pend @astart const char * filename - a character string, the name of the file containing a JPEG image to read (input). @aend @dstart This routine will open and read a JPEG image file. If there are no errors, this routine will create and return to the caller an RMimage object containing the raster image data. If there is a problem reading the JPEG file, NULL is returned. @dend * ---------------------------------------------------- */ RMimage * rmiReadJPEG (const char * filename) { #if RM_JPEG int width, height; unsigned char *pixelData=NULL; RMimage *t=NULL; if (readJPEG (filename, &width, &height, &pixelData) == RM_CHILL) { t = rmImageNew(2, width, height, 1, RM_IMAGE_RGB, RM_UNSIGNED_BYTE, RM_COPY_DATA); #ifdef RM_WIN /* * Windows OpenGL implementations mandate 4-byte alignment of * pixel data per scanline. When we created the image (above), * the pixel data buffer is 4-byte aligned per scanline. We add * some code here to copy from the buffer returned by the * read jpeg routine to pad out to 4-byte scanline boundaries. */ { unsigned char *d, *s, *d2; int i,j; int swidth = rmImageGetBytesPerScanline(t); int extraBytes = swidth - width*3; d2 = d = (unsigned char *)malloc(sizeof(unsigned char)*height*swidth); s = pixelData; for (j=0;j