diff options
author | scuri <scuri> | 2010-01-26 15:56:35 +0000 |
---|---|---|
committer | scuri <scuri> | 2010-01-26 15:56:35 +0000 |
commit | 10c47ef2af59dfba47633520faa9302af90a9ae7 (patch) | |
tree | b232d571cffdc9d6d3481fac43965618faf957fc /src/libtiff/tif_jpeg.c | |
parent | 8530450bdbc42d17fa32f34e5fef0f980c71439b (diff) |
*** empty log message ***
Diffstat (limited to 'src/libtiff/tif_jpeg.c')
-rw-r--r-- | src/libtiff/tif_jpeg.c | 401 |
1 files changed, 155 insertions, 246 deletions
diff --git a/src/libtiff/tif_jpeg.c b/src/libtiff/tif_jpeg.c index 0068ad9..6a8ebea 100644 --- a/src/libtiff/tif_jpeg.c +++ b/src/libtiff/tif_jpeg.c @@ -1,4 +1,4 @@ -/* $Id: tif_jpeg.c,v 1.3 2009/12/11 15:17:41 scuri Exp $ */ +/* $Id: tif_jpeg.c,v 1.4 2010/01/26 15:56:36 scuri Exp $ */ /* * Copyright (c) 1994-1997 Sam Leffler @@ -154,7 +154,6 @@ typedef struct { TIFFVGetMethod vgetparent; /* super-class method */ TIFFVSetMethod vsetparent; /* super-class method */ - TIFFPrintMethod printdir; /* super-class method */ TIFFStripMethod defsparent; /* super-class method */ TIFFTileMethod deftparent; /* super-class method */ /* pseudo-tag fields */ @@ -228,7 +227,7 @@ TIFFjpeg_error_exit(j_common_ptr cinfo) char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) (cinfo, buffer); - TIFFErrorExt(sp->tif->tif_clientdata, "JPEGLib", "%s", buffer); /* display the error message */ + TIFFErrorExt(sp->tif->tif_clientdata, "JPEGLib", buffer); /* display the error message */ jpeg_abort(cinfo); /* clean up libjpeg state */ LONGJMP(sp->exit_jmpbuf, 1); /* return to libtiff caller */ } @@ -244,7 +243,7 @@ TIFFjpeg_output_message(j_common_ptr cinfo) char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message) (cinfo, buffer); - TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", "%s", buffer); + TIFFWarningExt(((JPEGState *) cinfo)->tif->tif_clientdata, "JPEGLib", buffer); } /* @@ -715,7 +714,7 @@ JPEGPreDecode(TIFF* tif, tsample_t s) } else { if (segment_height > td->td_rowsperstrip) segment_height = td->td_rowsperstrip; - sp->bytesperline = TIFFOldScanlineSize(tif); + sp->bytesperline = TIFFScanlineSize(tif); } if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) { /* @@ -725,29 +724,14 @@ JPEGPreDecode(TIFF* tif, tsample_t s) segment_width = TIFFhowmany(segment_width, sp->h_sampling); segment_height = TIFFhowmany(segment_height, sp->v_sampling); } - if (sp->cinfo.d.image_width < segment_width || - sp->cinfo.d.image_height < segment_height) { + if (sp->cinfo.d.image_width != segment_width || + sp->cinfo.d.image_height != segment_height) { TIFFWarningExt(tif->tif_clientdata, module, - "Improper JPEG strip/tile size, " - "expected %dx%d, got %dx%d", - segment_width, segment_height, - sp->cinfo.d.image_width, - sp->cinfo.d.image_height); - } - if (sp->cinfo.d.image_width > segment_width || - sp->cinfo.d.image_height > segment_height) { - /* - * This case could be dangerous, if the strip or tile size has - * been reported as less than the amount of data jpeg will - * return, some potential security issues arise. Catch this - * case and error out. - */ - TIFFErrorExt(tif->tif_clientdata, module, - "JPEG strip/tile size exceeds expected dimensions," - " expected %dx%d, got %dx%d", - segment_width, segment_height, - sp->cinfo.d.image_width, sp->cinfo.d.image_height); - return (0); + "Improper JPEG strip/tile size, expected %dx%d, got %dx%d", + segment_width, + segment_height, + sp->cinfo.d.image_width, + sp->cinfo.d.image_height); } if (sp->cinfo.d.num_components != (td->td_planarconfig == PLANARCONFIG_CONTIG ? @@ -779,24 +763,6 @@ JPEGPreDecode(TIFF* tif, tsample_t s) sp->cinfo.d.comp_info[0].v_samp_factor, sp->h_sampling, sp->v_sampling); - /* - * There are potential security issues here - * for decoders that have already allocated - * buffers based on the expected sampling - * factors. Lets check the sampling factors - * dont exceed what we were expecting. - */ - if (sp->cinfo.d.comp_info[0].h_samp_factor - > sp->h_sampling - || sp->cinfo.d.comp_info[0].v_samp_factor - > sp->v_sampling) { - TIFFErrorExt(tif->tif_clientdata, - module, - "Cannot honour JPEG sampling factors" - " that exceed those specified."); - return (0); - } - /* * XXX: Files written by the Intergraph software * has different sampling factors stored in the @@ -844,9 +810,10 @@ JPEGPreDecode(TIFF* tif, tsample_t s) /* Suppress colorspace handling */ sp->cinfo.d.jpeg_color_space = JCS_UNKNOWN; sp->cinfo.d.out_color_space = JCS_UNKNOWN; + tif->tif_flags |= TIFF_UPSAMPLED; /* IMLIB - allow upsampling when there is no colorspace handling if (td->td_planarconfig == PLANARCONFIG_CONTIG && (sp->h_sampling != 1 || sp->v_sampling != 1)) - downsampled_output = TRUE; + downsampled_output = TRUE; */ /* XXX what about up-sampling? */ } if (downsampled_output) { @@ -986,121 +953,119 @@ JPEGDecode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) /*ARGSUSED*/ static int JPEGDecodeRaw(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) { - JPEGState *sp = JState(tif); - tsize_t nrows; - (void) s; - - /* data is expected to be read in multiples of a scanline */ - if ( (nrows = sp->cinfo.d.image_height) ) { - /* Cb,Cr both have sampling factors 1, so this is correct */ - JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width; - int samples_per_clump = sp->samplesperclump; + JPEGState *sp = JState(tif); + tsize_t nrows; + (void) s; + /* data is expected to be read in multiples of a scanline */ + if ( (nrows = sp->cinfo.d.image_height) ) { + /* Cb,Cr both have sampling factors 1, so this is correct */ + JDIMENSION clumps_per_line = sp->cinfo.d.comp_info[1].downsampled_width; + int samples_per_clump = sp->samplesperclump; + #ifdef JPEG_LIB_MK1 - unsigned short* tmpbuf = _TIFFmalloc(sizeof(unsigned short) * - sp->cinfo.d.output_width * - sp->cinfo.d.num_components); + unsigned short* tmpbuf = _TIFFmalloc(sizeof(unsigned short) * + sp->cinfo.d.output_width * + sp->cinfo.d.num_components); #endif - - do { - jpeg_component_info *compptr; - int ci, clumpoffset; - - /* Reload downsampled-data buffer if needed */ - if (sp->scancount >= DCTSIZE) { - int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE; - if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) != n) - return (0); - sp->scancount = 0; - } - /* - * Fastest way to unseparate data is to make one pass - * over the scanline for each row of each component. - */ - clumpoffset = 0; /* first sample in clump */ - for (ci = 0, compptr = sp->cinfo.d.comp_info; - ci < sp->cinfo.d.num_components; - ci++, compptr++) { - int hsamp = compptr->h_samp_factor; - int vsamp = compptr->v_samp_factor; - int ypos; - - for (ypos = 0; ypos < vsamp; ypos++) { - JSAMPLE *inptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos]; + + do { + jpeg_component_info *compptr; + int ci, clumpoffset; + + /* Reload downsampled-data buffer if needed */ + if (sp->scancount >= DCTSIZE) { + int n = sp->cinfo.d.max_v_samp_factor * DCTSIZE; + if (TIFFjpeg_read_raw_data(sp, sp->ds_buffer, n) + != n) + return (0); + sp->scancount = 0; + } + /* + * Fastest way to unseparate data is to make one pass + * over the scanline for each row of each component. + */ + clumpoffset = 0; /* first sample in clump */ + for (ci = 0, compptr = sp->cinfo.d.comp_info; + ci < sp->cinfo.d.num_components; + ci++, compptr++) { + int hsamp = compptr->h_samp_factor; + int vsamp = compptr->v_samp_factor; + int ypos; + + for (ypos = 0; ypos < vsamp; ypos++) { + JSAMPLE *inptr = sp->ds_buffer[ci][sp->scancount*vsamp + ypos]; #ifdef JPEG_LIB_MK1 - JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset; + JSAMPLE *outptr = (JSAMPLE*)tmpbuf + clumpoffset; #else - JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset; + JSAMPLE *outptr = (JSAMPLE*)buf + clumpoffset; #endif - JDIMENSION nclump; - - if (hsamp == 1) { - /* fast path for at least Cb and Cr */ - for (nclump = clumps_per_line; nclump-- > 0; ) { - outptr[0] = *inptr++; - outptr += samples_per_clump; - } - } else { - int xpos; - - /* general case */ - for (nclump = clumps_per_line; nclump-- > 0; ) { - for (xpos = 0; xpos < hsamp; xpos++) - outptr[xpos] = *inptr++; - outptr += samples_per_clump; - } - } - clumpoffset += hsamp; - } - } + JDIMENSION nclump; + + if (hsamp == 1) { + /* fast path for at least Cb and Cr */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + outptr[0] = *inptr++; + outptr += samples_per_clump; + } + } else { + int xpos; + + /* general case */ + for (nclump = clumps_per_line; nclump-- > 0; ) { + for (xpos = 0; xpos < hsamp; xpos++) + outptr[xpos] = *inptr++; + outptr += samples_per_clump; + } + } + clumpoffset += hsamp; + } + } #ifdef JPEG_LIB_MK1 - { - if (sp->cinfo.d.data_precision == 8) - { - int i=0; - int len = sp->cinfo.d.output_width * sp->cinfo.d.num_components; - for (i=0; i<len; i++) - { - ((unsigned char*)buf)[i] = tmpbuf[i] & 0xff; - } - } - else - { // 12-bit - int value_pairs = (sp->cinfo.d.output_width - * sp->cinfo.d.num_components) / 2; - int iPair; - for( iPair = 0; iPair < value_pairs; iPair++ ) - { - unsigned char *out_ptr = ((unsigned char *) buf) + iPair * 3; - JSAMPLE *in_ptr = tmpbuf + iPair * 2; - out_ptr[0] = (in_ptr[0] & 0xff0) >> 4; - out_ptr[1] = ((in_ptr[0] & 0xf) << 4) - | ((in_ptr[1] & 0xf00) >> 8); - out_ptr[2] = ((in_ptr[1] & 0xff) >> 0); - } - } - } + { + if (sp->cinfo.d.data_precision == 8) + { + int i=0; + int len = sp->cinfo.d.output_width * sp->cinfo.d.num_components; + for (i=0; i<len; i++) + { + ((unsigned char*)buf)[i] = tmpbuf[i] & 0xff; + } + } + else + { // 12-bit + int value_pairs = (sp->cinfo.d.output_width + * sp->cinfo.d.num_components) / 2; + int iPair; + for( iPair = 0; iPair < value_pairs; iPair++ ) + { + unsigned char *out_ptr = ((unsigned char *) buf) + iPair * 3; + JSAMPLE *in_ptr = tmpbuf + iPair * 2; + out_ptr[0] = (in_ptr[0] & 0xff0) >> 4; + out_ptr[1] = ((in_ptr[0] & 0xf) << 4) + | ((in_ptr[1] & 0xf00) >> 8); + out_ptr[2] = ((in_ptr[1] & 0xff) >> 0); + } + } + } #endif - sp->scancount ++; - tif->tif_row += sp->v_sampling; - /* increment/decrement of buf and cc is still incorrect, but should not matter - * TODO: resolve this */ - buf += sp->bytesperline; - cc -= sp->bytesperline; - nrows -= sp->v_sampling; - } while (nrows > 0); - + ++sp->scancount; + ++tif->tif_row; + buf += sp->bytesperline; + cc -= sp->bytesperline; + } while (--nrows > 0); + #ifdef JPEG_LIB_MK1 - _TIFFfree(tmpbuf); + _TIFFfree(tmpbuf); #endif - } + } - /* Close down the decompressor if done. */ - return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height - || TIFFjpeg_finish_decompress(sp); + /* Close down the decompressor if done. */ + return sp->cinfo.d.output_scanline < sp->cinfo.d.output_height + || TIFFjpeg_finish_decompress(sp); } @@ -1235,9 +1200,9 @@ JPEGSetupEncode(TIFF* tif) /* BITS_IN_JSAMPLE now permits 8 and 12 --- dgilbert */ if (td->td_bitspersample != 8 && td->td_bitspersample != 12) #else - if (td->td_bitspersample != BITS_IN_JSAMPLE ) + if (td->td_bitspersample != BITS_IN_JSAMPLE ) #endif - { + { TIFFErrorExt(tif->tif_clientdata, module, "BitsPerSample %d not allowed for JPEG", (int) td->td_bitspersample); return (0); @@ -1315,7 +1280,7 @@ JPEGPreEncode(TIFF* tif, tsample_t s) segment_height = td->td_imagelength - tif->tif_row; if (segment_height > td->td_rowsperstrip) segment_height = td->td_rowsperstrip; - sp->bytesperline = TIFFOldScanlineSize(tif); + sp->bytesperline = TIFFScanlineSize(tif); } if (td->td_planarconfig == PLANARCONFIG_SEPARATE && s > 0) { /* for PC 2, scale down the strip/tile size @@ -1338,8 +1303,9 @@ JPEGPreEncode(TIFF* tif, tsample_t s) sp->cinfo.c.in_color_space = JCS_RGB; } else { sp->cinfo.c.in_color_space = JCS_YCbCr; + tif->tif_flags |= TIFF_UPSAMPLED; /* IMLIB - allow upsampling in the input data if (sp->h_sampling != 1 || sp->v_sampling != 1) - downsampled_input = TRUE; + downsampled_input = TRUE; */ } if (!TIFFjpeg_set_colorspace(sp, JCS_YCbCr)) return (0); @@ -1427,10 +1393,6 @@ JPEGEncode(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) if (cc % sp->bytesperline) TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded"); - /* The last strip will be limited to image size */ - if( !isTiled(tif) && tif->tif_row+nrows > tif->tif_dir.td_imagelength ) - nrows = tif->tif_dir.td_imagelength - tif->tif_row; - while (nrows-- > 0) { bufptr[0] = (JSAMPROW) buf; if (TIFFjpeg_write_scanlines(sp, bufptr, 1) != 1) @@ -1457,25 +1419,18 @@ JPEGEncodeRaw(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) int clumpoffset, ci, xpos, ypos; jpeg_component_info* compptr; int samples_per_clump = sp->samplesperclump; - tsize_t bytesperclumpline; (void) s; assert(sp != NULL); - /* data is expected to be supplied in multiples of a clumpline */ - /* a clumpline is equivalent to v_sampling desubsampled scanlines */ - /* TODO: the following calculation of bytesperclumpline, should substitute calculation of sp->bytesperline, except that it is per v_sampling lines */ - bytesperclumpline = (((sp->cinfo.c.image_width+sp->h_sampling-1)/sp->h_sampling) - *(sp->h_sampling*sp->v_sampling+2)*sp->cinfo.c.data_precision+7) - /8; - - nrows = ( cc / bytesperclumpline ) * sp->v_sampling; - if (cc % bytesperclumpline) + /* data is expected to be supplied in multiples of a scanline */ + nrows = cc / sp->bytesperline; + if (cc % sp->bytesperline) TIFFWarningExt(tif->tif_clientdata, tif->tif_name, "fractional scanline discarded"); /* Cb,Cr both have sampling factors 1, so this is correct */ clumps_per_line = sp->cinfo.c.comp_info[1].downsampled_width; - while (nrows > 0) { + while (nrows-- > 0) { /* * Fastest way to separate the data is to make one pass * over the scanline for each row of each component. @@ -1520,9 +1475,9 @@ JPEGEncodeRaw(TIFF* tif, tidata_t buf, tsize_t cc, tsample_t s) return (0); sp->scancount = 0; } - tif->tif_row += sp->v_sampling; + if (nrows > 0) + tif->tif_row++; buf += sp->bytesperline; - nrows -= sp->v_sampling; } return (1); } @@ -1574,7 +1529,6 @@ JPEGCleanup(TIFF* tif) tif->tif_tagmethods.vgetfield = sp->vgetparent; tif->tif_tagmethods.vsetfield = sp->vsetparent; - tif->tif_tagmethods.printdir = sp->printdir; if( sp->cinfo_initialized ) TIFFjpeg_destroy(sp); /* release libjpeg resources */ @@ -1586,43 +1540,11 @@ JPEGCleanup(TIFF* tif) _TIFFSetDefaultCompressionState(tif); } -static void -JPEGResetUpsampled( TIFF* tif ) -{ - JPEGState* sp = JState(tif); - TIFFDirectory* td = &tif->tif_dir; - - /* - * Mark whether returned data is up-sampled or not so TIFFStripSize - * and TIFFTileSize return values that reflect the true amount of - * data. - */ - tif->tif_flags &= ~TIFF_UPSAMPLED; - if (td->td_planarconfig == PLANARCONFIG_CONTIG) { - if (td->td_photometric == PHOTOMETRIC_YCBCR && - sp->jpegcolormode == JPEGCOLORMODE_RGB) { - tif->tif_flags |= TIFF_UPSAMPLED; - } else { -#ifdef notdef - if (td->td_ycbcrsubsampling[0] != 1 || - td->td_ycbcrsubsampling[1] != 1) - ; /* XXX what about up-sampling? */ -#endif - } - } - - /* - * Must recalculate cached tile size in case sampling state changed. - * Should we really be doing this now if image size isn't set? - */ - tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1; -} - static int JPEGVSetField(TIFF* tif, ttag_t tag, va_list ap) { JPEGState* sp = JState(tif); - const TIFFFieldInfo* fip; + TIFFDirectory* td = &tif->tif_dir; uint32 v32; assert(sp != NULL); @@ -1644,21 +1566,34 @@ JPEGVSetField(TIFF* tif, ttag_t tag, va_list ap) return (1); /* pseudo tag */ case TIFFTAG_JPEGCOLORMODE: sp->jpegcolormode = va_arg(ap, int); - JPEGResetUpsampled( tif ); + /* + * Mark whether returned data is up-sampled or not + * so TIFFStripSize and TIFFTileSize return values + * that reflect the true amount of data. + */ + tif->tif_flags &= ~TIFF_UPSAMPLED; + if (td->td_planarconfig == PLANARCONFIG_CONTIG) { + if (td->td_photometric == PHOTOMETRIC_YCBCR && + sp->jpegcolormode == JPEGCOLORMODE_RGB) { + tif->tif_flags |= TIFF_UPSAMPLED; + } else { + if (td->td_ycbcrsubsampling[0] != 1 || + td->td_ycbcrsubsampling[1] != 1) + ; /* XXX what about up-sampling? */ + } + } + /* + * Must recalculate cached tile size + * in case sampling state changed. + */ + tif->tif_tilesize = isTiled(tif) ? TIFFTileSize(tif) : (tsize_t) -1; return (1); /* pseudo tag */ - case TIFFTAG_PHOTOMETRIC: - { - int ret_value = (*sp->vsetparent)(tif, tag, ap); - JPEGResetUpsampled( tif ); - return ret_value; - } case TIFFTAG_JPEGTABLESMODE: sp->jpegtablesmode = va_arg(ap, int); return (1); /* pseudo tag */ case TIFFTAG_YCBCRSUBSAMPLING: /* mark the fact that we have a real ycbcrsubsampling! */ sp->ycbcrsampling_fetched = 1; - /* should we be recomputing upsampling info here? */ return (*sp->vsetparent)(tif, tag, ap); case TIFFTAG_FAXRECVPARAMS: sp->recvparams = va_arg(ap, uint32); @@ -1675,13 +1610,7 @@ JPEGVSetField(TIFF* tif, ttag_t tag, va_list ap) default: return (*sp->vsetparent)(tif, tag, ap); } - - if ((fip = _TIFFFieldWithTag(tif, tag))) { - TIFFSetFieldBit(tif, fip->field_bit); - } else { - return (0); - } - + TIFFSetFieldBit(tif, _TIFFFieldWithTag(tif, tag)->field_bit); tif->tif_flags |= TIFF_DIRTYDIRECT; return (1); } @@ -1777,6 +1706,7 @@ JPEGVGetField(TIFF* tif, ttag_t tag, va_list ap) case TIFFTAG_YCBCRSUBSAMPLING: JPEGFixupTestSubsampling( tif ); return (*sp->vgetparent)(tif, tag, ap); + break; case TIFFTAG_FAXRECVPARAMS: *va_arg(ap, uint32*) = sp->recvparams; break; @@ -1870,18 +1800,8 @@ static int JPEGInitializeLibJPEG( TIFF * tif, int force_encode, int force_decode int data_is_empty = TRUE; int decompress; - - if(sp->cinfo_initialized) - { - if( force_encode && sp->cinfo.comm.is_decompressor ) - TIFFjpeg_destroy( sp ); - else if( force_decode && !sp->cinfo.comm.is_decompressor ) - TIFFjpeg_destroy( sp ); - else - return 1; - - sp->cinfo_initialized = 0; - } + if( sp->cinfo_initialized ) + return 1; /* * Do we have tile data already? Make sure we initialize the @@ -1937,38 +1857,28 @@ TIFFInitJPEG(TIFF* tif, int scheme) assert(scheme == COMPRESSION_JPEG); /* - * Merge codec-specific tag information. - */ - if (!_TIFFMergeFieldInfo(tif, jpegFieldInfo, N(jpegFieldInfo))) { - TIFFErrorExt(tif->tif_clientdata, - "TIFFInitJPEG", - "Merging JPEG codec-specific tags failed"); - return 0; - } - - /* * Allocate state block so tag methods have storage to record values. */ tif->tif_data = (tidata_t) _TIFFmalloc(sizeof (JPEGState)); if (tif->tif_data == NULL) { - TIFFErrorExt(tif->tif_clientdata, - "TIFFInitJPEG", "No space for JPEG state block"); - return 0; + TIFFErrorExt(tif->tif_clientdata, "TIFFInitJPEG", "No space for JPEG state block"); + return (0); } - _TIFFmemset(tif->tif_data, 0, sizeof(JPEGState)); + _TIFFmemset( tif->tif_data, 0, sizeof(JPEGState)); sp = JState(tif); sp->tif = tif; /* back link */ /* - * Override parent get/set field methods. + * Merge codec-specific tag information and override parent get/set + * field methods. */ + _TIFFMergeFieldInfo(tif, jpegFieldInfo, N(jpegFieldInfo)); sp->vgetparent = tif->tif_tagmethods.vgetfield; tif->tif_tagmethods.vgetfield = JPEGVGetField; /* hook for codec tags */ sp->vsetparent = tif->tif_tagmethods.vsetfield; tif->tif_tagmethods.vsetfield = JPEGVSetField; /* hook for codec tags */ - sp->printdir = tif->tif_tagmethods.printdir; tif->tif_tagmethods.printdir = JPEGPrintDir; /* hook for codec tags */ /* Default values for codec-specific fields */ @@ -2034,4 +1944,3 @@ TIFFInitJPEG(TIFF* tif, int scheme) #endif /* JPEG_SUPPORT */ /* vim: set ts=8 sts=8 sw=8 noet: */ - |