1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18  *
19  * Original Contributor:
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  * Additional Contributor(s):
22  *
23  * The OpenRM project is located at http://openrm.sourceforge.net/.
24  */
25 /*
26  * $Id: rmjpeg.c,v 1.9 2005/02/19 16:10:45 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.9 $
29  * $Log: rmjpeg.c,v $
30  * Revision 1.9  2005/02/19 16:10:45  wes
31  * Distro sync and consolidation.
32  *
33  * Revision 1.8  2005/01/23 17:09:39  wes
34  * Copyright update to 2005.
35  *
36  * Revision 1.7  2004/03/10 01:49:53  wes
37  * Updated documentation on rmiWriteJPEG.
38  *
39  * Revision 1.6  2004/01/17 04:09:05  wes
40  * Updated copyright line for 2004.
41  *
42  * Revision 1.5  2003/06/20 01:38:14  wes
43  * Added code to rmiWriteJPEG that will permit only RMimages with
44  * RM_IMAGE_RGB format pixels to be processed. All other formats will
45  * produce an error message. Future work might be to accommodate a greater
46  * breadth of RMimage pixel formats.
47  *
48  * Revision 1.4  2003/06/19 20:53:56  wes
49  * Minor documentation tweaks.
50  *
51  * Revision 1.3  2003/02/14 00:21:20  wes
52  * Minor code cleanups.
53  *
54  * Revision 1.2  2003/02/02 02:07:21  wes
55  * Updated copyright to 2003.
56  *
57  * Revision 1.1.1.1  2003/01/28 02:15:23  wes
58  * Manual rebuild of rm150 repository.
59  *
60  * Revision 1.4  2003/01/16 22:21:19  wes
61  * Updated all source files to reflect new organization of header files:
62  * all header files formerly located in include/rmaux, include/rmi, include/rmv
63  * are now located in include/rm.
64  *
65  * Revision 1.3  2002/06/02 15:18:45  wes
66  * Fixed minor indexing bug.
67  *
68  * Revision 1.2  2001/06/04 01:00:05  wes
69  * Added documentation for JPEG i/o routines.
70  *
71  * Revision 1.1  2001/06/03 20:18:15  wes
72  * Initial entry (v140-beta1)
73  *
74  */
75 
76 #include <rm/rm.h>
77 #include <rm/rmi.h>
78 
79 #include <setjmp.h>		/* ugh */
80 
81 #if RM_JPEG
82 struct my_error_mgr {
83   struct jpeg_error_mgr pub;	/* "public" fields */
84   jmp_buf setjmp_buffer;	/* for return to caller */
85 };
86 
87 typedef struct my_error_mgr * my_error_ptr;
88 #endif
89 
90 /*
91  * Here's the routine that will replace the standard error_exit method:
92  */
93 #if RM_JPEG
94 METHODDEF(void)
my_error_exit(j_common_ptr cinfo)95 my_error_exit (j_common_ptr cinfo)
96 {
97 
98   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
99   my_error_ptr myerr = (my_error_ptr) cinfo->err;
100 
101   /* Always display the message. */
102   /* We could postpone this until after returning, if we chose. */
103   (*cinfo->err->output_message) (cinfo);
104 
105   /* Return control to the setjmp point */
106   longjmp(myerr->setjmp_buffer, 1);
107 }
108 #endif
109 
110 /*
111  * ----------------------------------------------------
112  * @Name rmiWriteJPEG
113  @pstart
114  RMenum  rmiWriteJPEG(const char *filename,
115                       int quality,
116                       const RMimage *toWrite)
117  @pend
118 
119  @astart
120 
121  const char *filename - the name of the file to be written.
122 
123  int quality - integer value that controls degree of lossiness in
124  JPEG file. The value of quality ranges from 0-100, with 100 being
125  the least amount of loss, and smaller numbers producing increasing
126  amounts of error. Smaller numbers result in smaller output file size.
127 
128  const RMimage *toWrite - the RMimage object that will be written
129  to the file in JPEG format.
130 
131  @aend
132 
133  @dstart
134  Writes a JPEG image raster file named "filename" using the input RMimage
135  object "toWrite" as the source. The quality of the JPEG file is determined
136  by the "quality" parameter, which ranges from 0-100. Smaller quality values
137  produce greater amounts of compression, smaller files and poorer image
138  image quality. Larger quality values produce larger files, less compression,
139  and better image quality.
140 
141  Returns RM_CHILL upon success, or RM_WHACKED upon failure.
142 
143  This routine works only on RMimage objects that contain RGB images.
144  @dend
145  * ----------------------------------------------------
146  */
147 RMenum
rmiWriteJPEG(const char * filename,int quality,const RMimage * toWrite)148 rmiWriteJPEG(const char *filename,
149 	     int quality,
150 	     const RMimage *toWrite)
151 {
152 #if RM_JPEG
153     int image_width;
154     int image_height;
155     unsigned char * pixeldata;
156 
157     /*  this code is more or less a cut-n-paste of the decompress file()
158 	routine in the example.c file shipped with the jpeg-6b source.
159 	comments have been left pretty much intact. */
160 
161     struct jpeg_compress_struct cinfo;
162 
163     /* This struct represents a JPEG error handler.  It is declared separately
164      * because applications often want to supply a specialized error handler
165      * (see the second half of this file for an example).  But here we just
166      * take the easy way out and use the standard error handler, which will
167      * print a message on stderr and call exit() if compression fails.
168      * Note that this struct must live as long as the main JPEG parameter
169      * struct, to avoid dangling-pointer problems.
170      */
171     struct jpeg_error_mgr jerr;
172     /* More stuff */
173     FILE * outfile;		/* target file */
174     JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
175     int j;
176     int bytesPerScanline;       /* physical row width in image buffer */
177 
178     if ((rmImageGetImageSize(toWrite, NULL, &image_width, &image_height, NULL, NULL, NULL) == RM_WHACKED) ||
179 	((pixeldata = (unsigned char *)rmImageGetPixelData(toWrite)) == NULL))
180     {
181 	rmError("rmiWriteJPEGFile() : error reading image dimensions or pixel data. No JPEG file is being produced. ");
182 	return(RM_WHACKED);
183     }
184 
185     /* next, check for valid pixel format and type */
186     {
187 	RMenum pFormat, pType;
188 
189 	pFormat = rmImageGetFormat (toWrite);
190 	pType = rmImageGetType (toWrite);
191 
192 	if (pFormat != RM_IMAGE_RGB)
193 	{
194 	    rmError("rmiWriteJPEG() error: the input RMimage does not have RM_IMAGE_RGB format pixels, unable to write the JPEG file. ");
195 	    return RM_WHACKED;
196 	}
197 
198 	if (pType != RM_UNSIGNED_BYTE)
199 	{
200 	    rmError("rmiWriteJPEG() error: the input RMimage does not have RM_UNSIGNED_BYTE pixels, unable to write the JPEG file. ");
201 	    return RM_WHACKED;
202 	}
203     }
204 
205 
206 
207     /* Step 1: allocate and initialize JPEG compression object */
208 
209     /* We have to set up the error handler first, in case the initialization
210      * step fails.  (Unlikely, but it could happen if you are out of memory.)
211      * This routine fills in the contents of struct jerr, and returns jerr's
212      * address which we place into the link field in cinfo.
213      */
214     cinfo.err = jpeg_std_error(&jerr);
215     /* Now we can initialize the JPEG compression object. */
216     jpeg_create_compress(&cinfo);
217 
218     /* Step 2: specify data destination (eg, a file) */
219     /* Note: steps 2 and 3 can be done in either order. */
220 
221     /* Here we use the library-supplied code to send compressed data to a
222      * stdio stream.  You can also write your own code to do something else.
223      * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
224      * requires it in order to write binary files.
225      */
226     if ((outfile = fopen(filename, "wb")) == NULL)
227     {
228 	char buf[1024];
229 
230 	sprintf(buf,"rmiWriteJPEGFile(): can't open output file %s. ",filename);
231 	rmError(buf);
232 	return(RM_WHACKED);
233     }
234     jpeg_stdio_dest(&cinfo, outfile);
235 
236     /* Step 3: set parameters for compression */
237 
238     /* First we supply a description of the input image.
239      * Four fields of the cinfo struct must be filled in:
240      */
241     cinfo.image_width = image_width; 	/* image width and height, in pixels */
242     cinfo.image_height = image_height;
243     cinfo.input_components = 3;		/* # of color components per pixel */
244     cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
245 
246     /* Now use the library's routine to set default compression parameters.
247      * (You must set at least cinfo.in_color_space before calling this,
248      * since the defaults depend on the source color space.)
249      */
250     jpeg_set_defaults(&cinfo);
251     /* Now you can set any non-default parameters you wish to.
252      * Here we just illustrate the use of quality (quantization table) scaling:
253      */
254     jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
255 
256     /* Step 4: Start compressor */
257 
258     /* TRUE ensures that we will write a complete interchange-JPEG file.
259      * Pass TRUE unless you are very sure of what you're doing.
260      */
261     jpeg_start_compress(&cinfo, TRUE);
262 
263     /* Step 5: while (scan lines remain to be written) */
264     /*           jpeg_write_scanlines(...); */
265 
266     /* JSAMPLEs per row in image_buffer */
267     bytesPerScanline = rmImageGetBytesPerScanline(toWrite);
268 
269     for (j=0;j<image_height;j++)
270     {
271 	row_pointer[0] = pixeldata + (j * bytesPerScanline);
272 	(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
273     }
274 
275     /* Step 6: Finish compression */
276 
277     jpeg_finish_compress(&cinfo);
278     /* After finish_compress, we can close the output file. */
279     fclose(outfile);
280 
281     /* Step 7: release JPEG compression object */
282 
283     /* This is an important step since it will release a good deal of memory. */
284     jpeg_destroy_compress(&cinfo);
285 
286     /* And we're done! */
287 
288     return(RM_CHILL);
289 #else
290     rmWarning("rmiWriteJPEG warning: JPEG support has been intentionally disabled.");
291     return(RM_WHACKED);
292 #endif
293 }
294 
295 
296 #if RM_JPEG
297 static RMenum
readJPEG(const char * filename,int * widthReturn,int * heightReturn,unsigned char ** pixelDataReturn)298 readJPEG (const char * filename,
299 	  int *widthReturn,
300 	  int *heightReturn,
301 	  unsigned char **pixelDataReturn)
302 {
303     /* This struct contains the JPEG decompression parameters and pointers to
304      * working space (which is allocated as needed by the JPEG library).
305      */
306     struct jpeg_decompress_struct cinfo;
307     /* We use our private extension JPEG error handler.
308      * Note that this struct must live as long as the main JPEG parameter
309      * struct, to avoid dangling-pointer problems.
310      */
311 
312     struct my_error_mgr jerr;
313 
314     /* More stuff */
315     FILE * infile;		/* source file */
316     JSAMPARRAY buffer;		/* Output row buffer */
317     int row_stride;		/* physical row width in output buffer */
318     unsigned char *pd;
319     int indx=0;
320 
321     /* In this example we want to open the input file before doing anything else,
322      * so that the setjmp() error recovery below can assume the file is open.
323      * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
324      * requires it in order to read binary files.
325      */
326 
327     if ((infile = fopen(filename, "rb")) == NULL)
328 	return RM_WHACKED;
329 
330 
331     /* Step 1: allocate and initialize JPEG decompression object */
332 
333     /* We set up the normal JPEG error routines, then override error_exit. */
334     memset(&cinfo, 0, sizeof(struct jpeg_decompress_struct));
335     cinfo.err = jpeg_std_error(&jerr.pub);
336     jerr.pub.error_exit = my_error_exit;
337 
338     /* Establish the setjmp return context for my_error_exit to use. */
339     if (setjmp(jerr.setjmp_buffer)) {
340 	/* If we get here, the JPEG code has signaled an error.
341 	 * We need to clean up the JPEG object, close the input file, and return.
342 	 */
343 	jpeg_destroy_decompress(&cinfo);
344 	fclose(infile);
345 	return RM_WHACKED;
346     }
347 
348     /* Now we can initialize the JPEG decompression object. */
349     jpeg_create_decompress(&cinfo);
350 
351     /* Step 2: specify data source (eg, a file) */
352 
353     jpeg_stdio_src(&cinfo, infile);
354 
355     /* Step 3: read file parameters with jpeg_read_header() */
356 
357     (void) jpeg_read_header(&cinfo, TRUE);
358     /* We can ignore the return value from jpeg_read_header since
359      *   (a) suspension is not possible with the stdio data source, and
360      *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
361      * See libjpeg.doc for more info.
362      */
363 
364     /* Step 4: set parameters for decompression */
365 
366     /* In this example, we don't need to change any of the defaults set by
367      * jpeg_read_header(), so we do nothing here.
368      */
369 
370     /* Step 5: Start decompressor */
371 
372     (void) jpeg_start_decompress(&cinfo);
373     /* We can ignore the return value since suspension is not possible
374      * with the stdio data source.
375      */
376 
377     /* We may need to do some setup of our own at this point before reading
378      * the data.  After jpeg_start_decompress() we have the correct scaled
379      * output image dimensions available, as well as the output colormap
380      * if we asked for color quantization.
381      * In this example, we need to make an output work buffer of the right size.
382      */
383     /* JSAMPLEs per row in output buffer */
384     row_stride = cinfo.output_width * cinfo.output_components;
385     /* Make a one-row-high sample array that will go away when done with image */
386 
387     buffer = (*cinfo.mem->alloc_sarray)
388 	((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
389 
390     /* wes: config pixel buffer */
391     *widthReturn = cinfo.output_width;
392     *heightReturn = cinfo.output_height;
393     pd = (unsigned char *)malloc(sizeof(unsigned char)*cinfo.output_width*cinfo.output_height*cinfo.output_components);
394     indx = 0;
395 
396     /* Step 6: while (scan lines remain to be read) */
397     /*           jpeg_read_scanlines(...); */
398 
399     /* Here we use the library's state variable cinfo.output_scanline as the
400      * loop counter, so that we don't have to keep track ourselves.
401    */
402   while (cinfo.output_scanline < cinfo.output_height) {
403     /* jpeg_read_scanlines expects an array of pointers to scanlines.
404      * Here the array is only one element long, but you could ask for
405      * more than one scanline at a time if that's more convenient.
406      */
407       (void) jpeg_read_scanlines(&cinfo, buffer, 1);
408 
409       /* Assume put_scanline_someplace wants a pointer and sample count. */
410       /*    put_scanline_someplace(buffer[0], row_stride); */
411       memcpy(pd+indx, buffer[0], row_stride);
412       indx += row_stride;
413   }
414 
415 
416     /* Step 7: Finish decompression */
417     (void) jpeg_finish_decompress(&cinfo);
418     /* We can ignore the return value since suspension is not possible
419      * with the stdio data source.
420      */
421 
422     /* Step 8: Release JPEG decompression object */
423 
424     /* This is an important step since it will release a good deal of memory. */
425     jpeg_destroy_decompress(&cinfo);
426 
427     /* After finish_decompress, we can close the input file.
428      * Here we postpone it until after no more JPEG errors are possible,
429      * so as to simplify the setjmp error logic above.  (Actually, I don't
430      * think that jpeg_destroy can do an error exit, but why assume anything...)
431      */
432     fclose(infile);
433 
434     /* At this point you may want to check to see whether any corrupt-data
435      * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
436      */
437 
438     *pixelDataReturn = pd;
439     /* And we're done! */
440     return RM_CHILL;
441 }
442 #endif
443 
444 /*
445  * ----------------------------------------------------
446  * @Name rmiReadJPEG
447  @pstart
448  RMimage *rmiReadJPEG (const char * filename)
449  @pend
450 
451  @astart
452  const char * filename - a character string, the name of the file
453  containing a JPEG image to read (input).
454  @aend
455 
456  @dstart
457  This routine will open and read a JPEG image file. If there are
458  no errors, this routine will create and return to the caller an
459  RMimage object containing the raster image data. If there is a
460  problem reading the JPEG file, NULL is returned.
461  @dend
462  * ----------------------------------------------------
463  */
464 
465 RMimage *
rmiReadJPEG(const char * filename)466 rmiReadJPEG (const char * filename)
467 {
468 #if RM_JPEG
469     int width, height;
470     unsigned char *pixelData=NULL;
471     RMimage *t=NULL;
472 
473     if (readJPEG (filename, &width, &height, &pixelData) == RM_CHILL)
474     {
475 	t = rmImageNew(2, width, height, 1, RM_IMAGE_RGB, RM_UNSIGNED_BYTE, RM_COPY_DATA);
476 #ifdef RM_WIN
477 	/*
478 	 * Windows OpenGL implementations mandate 4-byte alignment of
479 	 * pixel data per scanline. When we created the image (above),
480 	 * the pixel data buffer is 4-byte aligned per scanline. We add
481 	 * some code here to copy from the buffer returned by the
482 	 * read jpeg routine to pad out to 4-byte scanline boundaries.
483 	 */
484 	{
485 	unsigned char *d, *s, *d2;
486 	int i,j;
487 	int swidth = rmImageGetBytesPerScanline(t);
488 	int extraBytes = swidth - width*3;
489 
490 	d2 = d = (unsigned char *)malloc(sizeof(unsigned char)*height*swidth);
491 
492 	s = pixelData;
493 	for (j=0;j<height;j++)
494 	{
495 	    memcpy(d, s, sizeof(unsigned char)*3*width);
496 	    d += width*3;
497 	    s += width*3;
498 	    d += extraBytes;
499 	}
500 
501 	rmImageSetPixelData(t, d2, RM_COPY_DATA, NULL);
502 	free((void *)d2);
503     }
504 #else
505 	rmImageSetPixelData(t, pixelData, RM_COPY_DATA, NULL);
506 #endif
507 	free((void *)pixelData);
508     }
509     return(t);
510 #else
511     rmWarning("rmiReadJPEG warning: JPEG support has been intentionally disabled.");
512     return(NULL);
513 #endif
514 }
515