| 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 <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 |
|
|---|
| 100 | typedef struct
|
|---|
| 101 | {
|
|---|
| 102 | unsigned int age;
|
|---|
| 103 | char str[JMSG_LENGTH_MAX];
|
|---|
| 104 | } MSG;
|
|---|
| 105 |
|
|---|
| 106 | /* the message buffer: */
|
|---|
| 107 | static MSG msgs[JMSG_MAX];
|
|---|
| 108 |
|
|---|
| 109 | /* replace oldest message with new message */
|
|---|
| 110 | static 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 |
|
|---|
| 131 | typedef 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 |
|
|---|
| 141 | METHODDEF(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 |
|
|---|
| 161 | METHODDEF(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 |
|
|---|
| 179 | typedef struct
|
|---|
| 180 | {
|
|---|
| 181 | struct jpeg_destination_mgr pub;
|
|---|
| 182 | int fd;
|
|---|
| 183 | JOCTET buf[DBUFSIZE];
|
|---|
| 184 | } DST;
|
|---|
| 185 |
|
|---|
| 186 | METHODDEF(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 |
|
|---|
| 193 | METHODDEF(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 |
|
|---|
| 203 | METHODDEF(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 |
|
|---|
| 212 | static 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 |
|
|---|
| 227 | typedef struct
|
|---|
| 228 | {
|
|---|
| 229 | struct jpeg_source_mgr pub;
|
|---|
| 230 | int fd;
|
|---|
| 231 | JOCTET buf[SBUFSIZE];
|
|---|
| 232 | BOOLEAN start_of_file;
|
|---|
| 233 | } SRC;
|
|---|
| 234 |
|
|---|
| 235 | METHODDEF(void) init_source(j_decompress_ptr dinfo)
|
|---|
| 236 | {
|
|---|
| 237 | SRC *s = (SRC *) dinfo->src;
|
|---|
| 238 | s->start_of_file = TRUE;
|
|---|
| 239 | }
|
|---|
| 240 |
|
|---|
| 241 | METHODDEF(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 |
|
|---|
| 259 | METHODDEF(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 |
|
|---|
| 274 | METHODDEF(void) term_source(j_decompress_ptr dinfo)
|
|---|
| 275 | {
|
|---|
| 276 | dinfo=dinfo; /* Suppress compiler warning */
|
|---|
| 277 | }
|
|---|
| 278 |
|
|---|
| 279 | static 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:*/
|
|---|
| 294 | static 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 |
|
|---|
| 302 | GBM_ERR jpg_qft(GBMFT *gbmft)
|
|---|
| 303 | {
|
|---|
| 304 | *gbmft = jpg_gbmft;
|
|---|
| 305 | return GBM_ERR_OK;
|
|---|
| 306 | }
|
|---|
| 307 | /*...e*/
|
|---|
| 308 | /*...sjpg_rhdr:0:*/
|
|---|
| 309 | GBM_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 |
|
|---|
| 374 | GBM_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 |
|
|---|
| 393 | GBM_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:*/
|
|---|
| 472 | GBM_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:*/
|
|---|
| 569 | const 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 |
|
|---|
| 586 | char jpg_missing;
|
|---|
| 587 |
|
|---|
| 588 | #endif
|
|---|