home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / gbmsrc.zip / gbmjpg.c < prev    next >
C/C++ Source or Header  |  1997-01-31  |  17KB  |  591 lines

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