/***************************************************************************** jpegdatasource.c ****************************************************************************** This is the data source manager that Jpegtopnm supplies to the JPEG library. The Jpeg library uses this object to get JPEG input. This data source manager is the same as the Jpeg library's built in "stdio" one, except that it looks ahead and one can query it to see if there is any data in the stream that the Jpeg library hasn't seen yet. Thus, you can use it in a program that reads multiple JPEG images and know when to stop. You can also nicely handle completely empty input files more gracefully than just crying input error. This data source manager does 4K fread() reads and passes 4K buffers to the Jpeg library. It reads one 4K block ahead, so there is up to 8K of image buffered at any time. By Bryan Henderson, San Jose CA 2002.10.13 *****************************************************************************/ #include #include #include #include /* Note: jpeglib.h prerequires stdlib.h and ctype.h. It should include them itself, but doesn't. */ #include #include "pm_c_util.h" #include "mallocvar.h" #include "pnm.h" #include "jpegdatasource.h" #define BUFFER_SIZE 4096 struct sourceManager { /* The following structure _must_ be the first thing in this structure, because the methods below assume the address of the struct jpeg_source_mgr is the same as the address of the struct sourceManager. The former address is the only information the Jpeg library gives these methods to tell them on what source manager object they are supposed to operate. */ struct jpeg_source_mgr jpegSourceMgr; FILE * ifP; bool prematureEof; /* We have been asked for data and were unable to comply because the file had no more to give (so we supplied EOI markers instead). */ JOCTET * currentBuffer; JOCTET * nextBuffer; unsigned int bytesInNextBuffer; JOCTET buffer1[BUFFER_SIZE]; JOCTET buffer2[BUFFER_SIZE]; }; static void dsInitSource(j_decompress_ptr const cinfoP) { /*---------------------------------------------------------------------------- This is the init_source method for the data source manager the Jpeg library uses. -----------------------------------------------------------------------------*/ /* The object was created ready to go and remains ready from one image to the next. No per-image initialization is necessary. */ } static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI}; /* An EOI (end of image) marker */ static boolean dsFillInputBuffer(j_decompress_ptr const cinfoP) { /*---------------------------------------------------------------------------- This is the fill_input_buffer method for the data source manager the Jpeg library uses. -----------------------------------------------------------------------------*/ struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src; if (srcP->bytesInNextBuffer == 0) { /* The decompressor expects more bytes, but there aren't any, so the file is corrupted -- probably truncated. We want the decompressor to decompress whatever it's read so far, so we synthesize an EOI marker here, but we also set error state in the source manager. The decompressor will recognize the truncation and pad out the image with gray. */ srcP->prematureEof = TRUE; srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker; srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker); } else { /* Rotate the buffers */ srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer; srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer; { JOCTET * tmp; tmp = srcP->nextBuffer; srcP->nextBuffer = srcP->currentBuffer; srcP->currentBuffer = tmp; } /* Fill the new "next" buffer */ srcP->bytesInNextBuffer = fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP); } return TRUE; } static void dsSkipInputData(j_decompress_ptr const cinfoP, long const num_bytes) { /*---------------------------------------------------------------------------- This is the skip_input_data method for the data source manager the Jpeg library uses. -----------------------------------------------------------------------------*/ struct jpeg_source_mgr * const jpegSourceMgrP = cinfoP->src; long i; for (i = 0; i < num_bytes; ++i) { if (jpegSourceMgrP->bytes_in_buffer == 0) dsFillInputBuffer(cinfoP); ++jpegSourceMgrP->next_input_byte; --jpegSourceMgrP->bytes_in_buffer; } } static void dsTermSource(j_decompress_ptr const cinfoP) { /*---------------------------------------------------------------------------- This is the term_source method for the data source manager the Jpeg library uses. -----------------------------------------------------------------------------*/ /* We couldn't care less that the Jpeg library is done reading an image. The source manager object remains active and ready for the Jpeg library to read the next image. */ } bool dsDataLeft(struct sourceManager * const srcP) { return((srcP->jpegSourceMgr.bytes_in_buffer > 0 || srcP->bytesInNextBuffer > 0)); } bool dsPrematureEof(struct sourceManager * const srcP) { return srcP->prematureEof; } struct sourceManager * dsCreateSource(const char * const fileName) { struct sourceManager * srcP; MALLOCVAR(srcP); if (srcP == NULL) pm_error("Unable to get memory for the Jpeg library source manager."); srcP->ifP = pm_openr(fileName); srcP->jpegSourceMgr.init_source = dsInitSource; srcP->jpegSourceMgr.fill_input_buffer = dsFillInputBuffer; srcP->jpegSourceMgr.skip_input_data = dsSkipInputData; srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart; srcP->jpegSourceMgr.term_source = dsTermSource; srcP->prematureEof = FALSE; srcP->currentBuffer = srcP->buffer1; srcP->nextBuffer = srcP->buffer2; srcP->jpegSourceMgr.bytes_in_buffer = fread(srcP->currentBuffer, 1, BUFFER_SIZE, srcP->ifP); srcP->jpegSourceMgr.next_input_byte = srcP->currentBuffer; srcP->bytesInNextBuffer = fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP); return srcP; } void dsDestroySource(struct sourceManager * const srcP) { pm_close(srcP->ifP); free(srcP); } struct jpeg_source_mgr * dsJpegSourceMgr(struct sourceManager * const srcP) { return &srcP->jpegSourceMgr; }