source: trunk/JPGPROC/source/gbmsrc/gbmjpg.c@ 4

Last change on this file since 4 was 2, checked in by stevenhl, 8 years ago

Import sources from cwmm-full.zip dated 2005-03-21

File size: 16.9 KB
Line 
1/*
2
3gbmjpg.c - JPEG File Interchange Format
4
5Credit for writing this module must go to Martin Lisowski,
6<mlisowsk@aixterm1.urz.uni-heidelberg.de>.
7
8This file is just as public domain as the rest of GBM.
9
10This module makes use of the work of the Independent JPEG group version 6a,
11ftp://sun2.urz.uni-heidelberg.de/pub/simtel/graphics/jpegsr6a.zip.
12This file was adapted from example.c of the JPEG lib.
13
14Compiling the IJG 6a using VisualAge C++ 3.0 for OS/2, I find I get quite
15a few warnings, which appear to be harmless so far.
16
17Reads JPEG-FIF images with 8 Bit per component (YUV)
18Also reads progressive JPEG images
19Also reads greyscale JPEG images (Y only)
20Writes JPEG-FIF images with 8 Bit per component (YUV)
21Currently does not write greyscale JPEG images
22
23Output Options: quality=# (0 to 100, default 75)
24 prog (write a simple progressive JPEG, default is not to)
25
26Errors returned by JPEGv6a are passed through. Warnings are ignored
27and GBM_ERR_OK is passed to the application. If DEBUG is set to
28true, the Warnings are printed to stdout. Errors are not printed
29to stdout. See my_output_message().
30
31Since receiving the initial version from Martin, I've :-
32 1. Incorporated it into the overall GBM source structure.
33 2. Compiled with VisualAge C++ (OS/2), xlc (AIX), Visual C++ (Win32).
34 3. Eliminated the fdopen problem via source/destination managers.
35 4. Extended the file extensions list to include others I've seen.
36 5. Made it a conditional part of the GBM build.
37 6. Comply with GBM source conventions.
38 7. Folded the source.
39 8. Avoided placing IJG datastructures in the gbm.priv as this causes
40 problems as to when the IJG datastructure should be cleaned up.
41 Side effect: header is scanned twice.
42 9. Fixed jpg_qft to return correct structure.
43
44*/
45
46#ifdef IJG
47
48#define DEBUG 0 /* set 1 to get debug-messages on stdout */
49
50/*...sincludes:0:*/
51#include <stdio.h>
52#include <ctype.h>
53#include <stddef.h>
54#include <stdlib.h>
55#include <string.h>
56#include <setjmp.h>
57#include "gbm.h"
58#include "gbmhelp.h"
59
60#include "jpeglib.h"
61#include "jinclude.h"
62#include "jerror.h"
63
64/* Note: We include more than just jpeglib.h because we are implementing
65 a source and a destination manager, which need to include the others. */
66/*...e*/
67
68/*...smessages:0:*/
69/* Error and warning messages generated by JPEGv6a are printf-like
70 strings, thus one also needs the parameters to display the message
71 correctly. The GBM-functions however only can return error-numbers
72 (GBM_ERR). To be able to give a complete error message to the user,
73 this module uses the following approach :-
74
75 When an error occurs, the printf-like message and its paramters
76 are expanded to a normal string. This string is saved in a
77 array called "message buffer" that can hold a certain amount of
78 such messages. Each array index in the message buffer corresponds
79 to an error code of type GBM_ERR:
80 error code = GBM_ERR_JPG_MESSAGE_0 + array index
81 This error code is returned to the application. The application
82 can obtain the message text by calling gbm_err() which in turn
83 calls jpg_err(). [ jpg_err() calculates the message buffer array
84 index by subracting GBM_ERR_JPG_MESSAGE_0 from the error code
85 and returns the message text. ]
86 This however can lead to a wrong message text if in the meantime
87 a new JPEGv6a error message has been inserted to the message
88 buffer at that same position.
89 To avoid this as much as possible, new messages always replace
90 the oldest message of the message buffer (see add_msg()). */
91
92/* max. number of messages stored in the
93 message buffer jpeg_error_messages[] */
94#define JMSG_MAX 20
95
96#define GBM_ERR_JPG_BAD_QUALITY ((GBM_ERR) 1900)
97#define GBM_ERR_JPG_MESSAGE_0 ((GBM_ERR) 1910)
98#define GBM_ERR_JPG_MESSAGE_LAST ((GBM_ERR) GBM_ERR_JPG_MESSAGE_0 + JMSG_MAX - 1)
99
100typedef struct
101 {
102 unsigned int age;
103 char str[JMSG_LENGTH_MAX];
104 } MSG;
105
106/* the message buffer: */
107static MSG msgs[JMSG_MAX];
108
109/* replace oldest message with new message */
110static GBM_ERR add_msg(const char *msg)
111 {
112 unsigned int max_age = 0;
113 int i, max_idx = 0;
114
115 /* search for the oldest message */
116 for ( i = 0; i < JMSG_MAX; i++ )
117 if ( ++msgs[i].age > max_age )
118 {
119 max_age = msgs[i].age;
120 max_idx = i;
121 }
122
123 /* replace with new message */
124 msgs[max_idx].age = 0;
125 strncpy(msgs[max_idx].str, msg, JMSG_LENGTH_MAX);
126 msgs[max_idx].str[JMSG_LENGTH_MAX-1] = '\0';
127
128 return GBM_ERR_JPG_MESSAGE_0 + max_idx;
129 }
130
131typedef struct
132 {
133 struct jpeg_error_mgr pub;
134 jmp_buf setjmp_buffer;
135 } ERR;
136
137/*
138 * Here's the routine that will replace the standard error_exit method:
139 */
140
141METHODDEF(void) my_error_exit(j_common_ptr cinfo)
142 {
143 ERR *e = (ERR *) cinfo->err;
144 char buffer[JMSG_LENGTH_MAX];
145
146 /* Create the message-string */
147 (*cinfo->err->format_message)(cinfo, buffer);
148
149 /* Return control to the setjmp point.
150 add_msg() saves the message text in the message buffer.
151 setjmp() will return apropriate GBM_ERR-code generated by add_msg().
152 Later, jpg_err() will return the message text, if given that
153 GBM_ERR-code. */
154 longjmp(e->setjmp_buffer, add_msg(buffer));
155 }
156
157/*
158 * Actual output of a warning or trace message.
159 */
160
161METHODDEF(void) my_output_message(j_common_ptr cinfo)
162 {
163 char buffer[JMSG_LENGTH_MAX];
164
165 /* Create the message */
166 (*cinfo->err->format_message) (cinfo, buffer);
167#if DEBUG
168 /* Send it to stdout, adding a newline */
169 printf("%s\n", buffer);
170#endif
171 }
172/*...e*/
173/*...sdestination manager:0:*/
174/* GBM uses file descriptors not streams for data output,
175 so implement a destination manager for use by the JPEG library. */
176
177#define DBUFSIZE 4096
178
179typedef struct
180 {
181 struct jpeg_destination_mgr pub;
182 int fd;
183 JOCTET buf[DBUFSIZE];
184 } DST;
185
186METHODDEF(void) init_destination(j_compress_ptr cinfo)
187 {
188 DST *d = (DST *) cinfo->dest;
189 d->pub.next_output_byte = d->buf;
190 d->pub.free_in_buffer = DBUFSIZE;
191 }
192
193METHODDEF(boolean) empty_output_buffer(j_compress_ptr cinfo)
194 {
195 DST *d = (DST *) cinfo->dest;
196 if ( gbm_file_write(d->fd, d->buf, DBUFSIZE) != DBUFSIZE )
197 ERREXIT(cinfo, JERR_FILE_WRITE);
198 d->pub.next_output_byte = d->buf;
199 d->pub.free_in_buffer = DBUFSIZE;
200 return TRUE;
201 }
202
203METHODDEF(void) term_destination(j_compress_ptr cinfo)
204 {
205 DST *d = (DST *) cinfo->dest;
206 int bytes = (DBUFSIZE-d->pub.free_in_buffer);
207 if ( bytes > 0 )
208 if ( gbm_file_write(d->fd, d->buf, bytes) != bytes )
209 ERREXIT(cinfo, JERR_FILE_WRITE);
210 }
211
212static void init_fd_dest(j_compress_ptr cinfo, int fd, DST *d)
213 {
214 cinfo->dest = (struct jpeg_destination_mgr *) d;
215 d->pub.init_destination = init_destination;
216 d->pub.empty_output_buffer = empty_output_buffer;
217 d->pub.term_destination = term_destination;
218 d->fd = fd;
219 }
220/*...e*/
221/*...ssource manager:0:*/
222/* GBM uses file descriptors not streams for data input,
223 so implement a source manager for use by the JPEG library. */
224
225#define SBUFSIZE 4096
226
227typedef struct
228 {
229 struct jpeg_source_mgr pub;
230 int fd;
231 JOCTET buf[SBUFSIZE];
232 BOOLEAN start_of_file;
233 } SRC;
234
235METHODDEF(void) init_source(j_decompress_ptr dinfo)
236 {
237 SRC *s = (SRC *) dinfo->src;
238 s->start_of_file = TRUE;
239 }
240
241METHODDEF(boolean) fill_input_buffer(j_decompress_ptr dinfo)
242 {
243 SRC *s = (SRC *) dinfo->src;
244 int bytes;
245 if ( (bytes = gbm_file_read(s->fd, s->buf, SBUFSIZE)) <= 0 )
246 {
247 if ( s->start_of_file )
248 ERREXIT(dinfo, JERR_INPUT_EMPTY);
249 s->buf[0] = (JOCTET) 0xff;
250 s->buf[1] = (JOCTET) JPEG_EOI;
251 bytes = 2;
252 }
253 s->pub.next_input_byte = s->buf;
254 s->pub.bytes_in_buffer = bytes;
255 s->start_of_file = FALSE;
256 return TRUE;
257 }
258
259METHODDEF(void) skip_input_data(j_decompress_ptr dinfo, long num_bytes)
260 {
261 SRC *s = (SRC *) dinfo->src;
262 if ( num_bytes > 0 )
263 {
264 while ( num_bytes > (long) s->pub.bytes_in_buffer )
265 {
266 num_bytes -= (long) s->pub.bytes_in_buffer;
267 fill_input_buffer(dinfo);
268 }
269 s->pub.next_input_byte += (size_t) num_bytes;
270 s->pub.bytes_in_buffer -= (size_t) num_bytes;
271 }
272 }
273
274METHODDEF(void) term_source(j_decompress_ptr dinfo)
275 {
276 dinfo=dinfo; /* Suppress compiler warning */
277 }
278
279static void init_fd_src(j_decompress_ptr dinfo, int fd, SRC *s)
280 {
281 dinfo->src = (struct jpeg_source_mgr *) s;
282 s->pub.init_source = init_source;
283 s->pub.fill_input_buffer = fill_input_buffer;
284 s->pub.skip_input_data = skip_input_data;
285 s->pub.resync_to_restart = jpeg_resync_to_restart;
286 s->pub.term_source = term_source;
287 s->pub.bytes_in_buffer = 0;
288 s->pub.next_input_byte = NULL;
289 s->fd = fd;
290 }
291/*...e*/
292
293/*...sjpg_qft:0:*/
294static GBMFT jpg_gbmft =
295 {
296 "JPEG",
297 "JPEG File Interchange Format",
298 "JPG JPEG JPE",
299 GBM_FT_R8|GBM_FT_R24|GBM_FT_W24,
300 };
301
302GBM_ERR jpg_qft(GBMFT *gbmft)
303 {
304 *gbmft = jpg_gbmft;
305 return GBM_ERR_OK;
306 }
307/*...e*/
308/*...sjpg_rhdr:0:*/
309GBM_ERR jpg_rhdr(const char *fn, int fd, GBM *gbm, const char *opt)
310 {
311 int jrc;
312 struct jpeg_decompress_struct dinfo;
313 ERR err;
314 SRC src;
315
316 fn=fn; opt=opt; /* Suppress compiler warnings */
317
318 /* Initialize the JPEG decompression object with default error handling. */
319 dinfo.err = jpeg_std_error((struct jpeg_error_mgr *)&err);
320 dinfo.err->output_message = my_output_message;
321 dinfo.err->error_exit = my_error_exit;
322 if ( (jrc = setjmp(err.setjmp_buffer)) != 0 )
323 {
324 /* If we get here, the JPEG code has signaled an error.
325 * We need to clean up the JPEG object and return.
326 */
327 jpeg_destroy_decompress(&dinfo);
328 return jrc;
329 }
330
331 jpeg_create_decompress(&dinfo);
332
333 /* Use a file descriptor based source manager */
334 init_fd_src(&dinfo, fd, &src);
335
336 /* Read file header, set default decompression parameters */
337 (void) jpeg_read_header(&dinfo, TRUE);
338 /* We can ignore the return value from jpeg_read_header since
339 * (a) suspension is not possible with the stdio data source, and
340 * (b) we passed TRUE to reject a tables-only JPEG file as an error.
341 * See libjpeg.doc for more info.
342 */
343
344 /* fill in GBM structure */
345#if DEBUG
346 printf("image color-space = %d\n", dinfo.jpeg_color_space);
347 printf("image components = %d\n", dinfo.num_components);
348 printf("output color-space = %d\n", dinfo.out_color_space);
349#endif
350 /* Start decompressor */
351 (void) jpeg_start_decompress(&dinfo);
352 /* We can ignore the return value since suspension is not possible
353 * with the stdio data source.
354 */
355
356 /* We may need to do some setup of our own at this point before reading
357 * the data. After jpeg_start_decompress() we have the correct scaled
358 * output image dimensions available, as well as the output colormap
359 * if we asked for color quantization.
360 */
361 gbm->w = dinfo.output_width;
362 gbm->h = dinfo.output_height;
363 gbm->bpp = dinfo.output_components * 8;
364#if DEBUG
365 printf("output components = %d\n", dinfo.output_components);
366#endif
367 jpeg_destroy_decompress(&dinfo);
368 return GBM_ERR_OK;
369 }
370/*...e*/
371/*...sjpg_rpal:0:*/
372/* If there is only one component (i.e. 8bpp), we have a greyscale picture */
373
374GBM_ERR jpg_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
375 {
376 fd=fd; /* Suppress compiler warning */
377 if ( gbm->bpp == 8 )
378 {
379 int p;
380 for ( p = 0; p < 0x100; p++ )
381 gbmrgb[p].r =
382 gbmrgb[p].g =
383 gbmrgb[p].b = (byte) p;
384 }
385 return GBM_ERR_OK;
386 }
387/*...e*/
388/*...sjpg_rdata:0:*/
389/* We re-read the header in order that the dinfo be set up correctly again.
390 We can't expect it in the gbm.priv, as jpg_rhdr may be called without
391 ever calling this routine. Who would clean up the dinfo in that case? */
392
393GBM_ERR jpg_rdata(int fd, GBM *gbm, byte *data)
394 {
395 struct jpeg_decompress_struct dinfo;
396 byte *c_data;
397 ERR err;
398 SRC src;
399 int stride, jrc;
400
401 gbm=gbm; /* Suppress compiler warning */
402
403 gbm_file_lseek(fd, 0L, SEEK_SET);
404
405 dinfo.err = jpeg_std_error((struct jpeg_error_mgr *)&err);
406 dinfo.err->output_message = my_output_message;
407 dinfo.err->error_exit = my_error_exit;
408 if ( (jrc = setjmp(err.setjmp_buffer)) != 0 )
409 {
410 /* If we get here, the JPEG code has signaled an error.
411 * We need to clean up the JPEG object and return.
412 */
413 jpeg_destroy_decompress(&dinfo);
414 return jrc;
415 }
416
417 jpeg_create_decompress(&dinfo);
418
419 /* Use a file descriptor based source manager */
420 init_fd_src(&dinfo, fd, &src);
421
422 /* Read file header, set default decompression parameters */
423 (void) jpeg_read_header(&dinfo, TRUE);
424
425 /* Start decompressor */
426 (void) jpeg_start_decompress(&dinfo);
427 /* We can ignore the return value since suspension is not possible
428 * with the stdio data source.
429 */
430
431 stride = ((dinfo.output_width * dinfo.output_components + 3) & ~3);
432
433 /* Process data */
434 c_data = data + (dinfo.output_height - 1) * stride;
435 /* Here we use the library's state variable dinfo.output_scanline as the
436 * loop counter, so that we don't have to keep track ourselves.
437 */
438 while ( dinfo.output_scanline < dinfo.output_height )
439 {
440 /* jpeg_read_scanlines expects an array of pointers to scanlines.
441 * Here the array is only one element long, but you could ask for
442 * more than one scanline at a time if that's more convenient.
443 */
444 int num_scanlines;
445 JSAMPROW sarray[1]; /* array of pointers to rows */
446 sarray[0] = c_data;
447 num_scanlines = jpeg_read_scanlines(&dinfo, sarray, 1);
448 c_data -= num_scanlines * stride;
449 /*
450 (*dest_mgr->put_pixel_rows) (&dinfo, dest_mgr, num_scanlines);
451 */
452 }
453
454 (void) jpeg_finish_decompress(&dinfo);
455 /* We can ignore the return value since suspension is not possible
456 * with the file descriptor data source.
457 */
458
459 /* This is an important step since it will release a good deal of memory. */
460 jpeg_destroy_decompress(&dinfo);
461
462 /* At this point you may want to check to see whether any corrupt-data
463 * warnings occurred (test whether err.pub.num_warnings is nonzero).
464 */
465#if DEBUG
466 printf("jpg_rdata: num_warnings=%ld\n",err.pub.num_warnings);
467#endif
468 return GBM_ERR_OK;
469 }
470/*...e*/
471/*...sjpg_w:0:*/
472GBM_ERR jpg_w(const char *fn, int fd, const GBM *gbm, const GBMRGB *gbmrgb, const byte *data, const char *opt)
473 {
474 int jrc;
475 int quality = 75;
476 int stride = ((gbm->w * 3 + 3) & ~3);
477 const char *index;
478 const byte *c_data = data + (gbm->h - 1) * stride;
479 struct jpeg_compress_struct cinfo;
480 ERR err;
481 DST dst;
482
483 fn=fn; gbmrgb=gbmrgb; /* Suppress compiler warnings */
484
485 if ( gbm->bpp != 24 )
486 return GBM_ERR_NOT_SUPP;
487
488 if ( (index = gbm_find_word_prefix(opt, "quality=")) != NULL )
489 {
490 sscanf(index + 8, "%d", &quality);
491 if ( quality < 0 || quality > 100 )
492 return GBM_ERR_JPG_BAD_QUALITY;
493 }
494
495 /* Initialize the JPEG compression object with default error handling. */
496 cinfo.err = jpeg_std_error((struct jpeg_error_mgr *)&err);
497 cinfo.err->output_message = my_output_message;
498 cinfo.err->error_exit = my_error_exit;
499 if ( (jrc = setjmp(err.setjmp_buffer)) != 0 )
500 {
501 /* If we get here, the JPEG code has signaled an error.
502 * We need to clean up the JPEG object and return.
503 */
504 jpeg_destroy_compress(&cinfo);
505 return jrc;
506 }
507
508 jpeg_create_compress(&cinfo);
509
510 init_fd_dest(&cinfo, fd, &dst);
511
512 /* First we supply a description of the input image.
513 * Four fields of the cinfo struct must be filled in:
514 */
515 cinfo.image_width = gbm->w; /* image width and height, in pixels */
516 cinfo.image_height = gbm->h;
517 cinfo.input_components = 3; /* # of color components per pixel */
518 cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
519 /* Now use the library's routine to set default compression parameters.
520 * (You must set at least cinfo.in_color_space before calling this,
521 * since the defaults depend on the source color space.)
522 */
523 jpeg_set_defaults(&cinfo);
524 /* Now you can set any non-default parameters you wish to.
525 * Here we just illustrate the use of quality (quantization table) scaling:
526 */
527 jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
528
529 /* Optionally allow simple progressive output. */
530 if ( gbm_find_word(opt, "prog") != NULL )
531 jpeg_simple_progression(&cinfo);
532
533 /* TRUE ensures that we will write a complete interchange-JPEG file.
534 * Pass TRUE unless you are very sure of what you're doing.
535 */
536 jpeg_start_compress(&cinfo, TRUE);
537
538 /* Here we use the library's state variable cinfo.next_scanline as the
539 * loop counter, so that we don't have to keep track ourselves.
540 * To keep things simple, we pass one scanline per call; you can pass
541 * more if you wish, though.
542 */
543
544 while ( cinfo.next_scanline < cinfo.image_height)
545 {
546 /* jpeg_write_scanlines expects an array of pointers to scanlines.
547 * Here the array is only one element long, but you could pass
548 * more than one scanline at a time if that's more convenient.
549 */
550 int num_scanlines;
551 JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
552 row_pointer[0] = (byte *) c_data;
553 /* you can ignore 'discards const' compiler
554 warning as jpeg_write_scanlines() doesn't
555 modify data at *(row_pointer[]) */
556 num_scanlines = jpeg_write_scanlines(&cinfo, row_pointer, 1);
557 c_data -= num_scanlines * stride;
558 }
559
560 jpeg_finish_compress(&cinfo);
561
562 /* This is an important step since it will release a good deal of memory. */
563 jpeg_destroy_compress(&cinfo);
564
565 return GBM_ERR_OK;
566 }
567/*...e*/
568/*...sjpg_err:0:*/
569const char *jpg_err(GBM_ERR rc)
570 {
571 switch ( rc )
572 {
573 case GBM_ERR_JPG_BAD_QUALITY:
574 return "quality is not in 0..100";
575 }
576 if ( rc >= GBM_ERR_JPG_MESSAGE_0 &&
577 rc <= GBM_ERR_JPG_MESSAGE_LAST )
578 return msgs[rc-GBM_ERR_JPG_MESSAGE_0].str;
579 else
580 return NULL;
581 }
582/*...e*/
583
584#else
585
586char jpg_missing;
587
588#endif
Note: See TracBrowser for help on using the repository browser.