michael@0: /* michael@0: * jdatadst.c michael@0: * michael@0: * This file was part of the Independent JPEG Group's software: michael@0: * Copyright (C) 1994-1996, Thomas G. Lane. michael@0: * Modified 2009-2012 by Guido Vollbeding. michael@0: * libjpeg-turbo Modifications: michael@0: * Copyright (C) 2013, D. R. Commander. michael@0: * For conditions of distribution and use, see the accompanying README file. michael@0: * michael@0: * This file contains compression data destination routines for the case of michael@0: * emitting JPEG data to memory or to a file (or any stdio stream). michael@0: * While these routines are sufficient for most applications, michael@0: * some will want to use a different destination manager. michael@0: * IMPORTANT: we assume that fwrite() will correctly transcribe an array of michael@0: * JOCTETs into 8-bit-wide elements on external storage. If char is wider michael@0: * than 8 bits on your machine, you may need to do some tweaking. michael@0: */ michael@0: michael@0: /* this is not a core library module, so it doesn't define JPEG_INTERNALS */ michael@0: #include "jinclude.h" michael@0: #include "jpeglib.h" michael@0: #include "jerror.h" michael@0: michael@0: #ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ michael@0: extern void * malloc JPP((size_t size)); michael@0: extern void free JPP((void *ptr)); michael@0: #endif michael@0: michael@0: michael@0: /* Expanded data destination object for stdio output */ michael@0: michael@0: typedef struct { michael@0: struct jpeg_destination_mgr pub; /* public fields */ michael@0: michael@0: FILE * outfile; /* target stream */ michael@0: JOCTET * buffer; /* start of buffer */ michael@0: } my_destination_mgr; michael@0: michael@0: typedef my_destination_mgr * my_dest_ptr; michael@0: michael@0: #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ michael@0: michael@0: michael@0: #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) michael@0: /* Expanded data destination object for memory output */ michael@0: michael@0: typedef struct { michael@0: struct jpeg_destination_mgr pub; /* public fields */ michael@0: michael@0: unsigned char ** outbuffer; /* target buffer */ michael@0: unsigned long * outsize; michael@0: unsigned char * newbuffer; /* newly allocated buffer */ michael@0: JOCTET * buffer; /* start of buffer */ michael@0: size_t bufsize; michael@0: } my_mem_destination_mgr; michael@0: michael@0: typedef my_mem_destination_mgr * my_mem_dest_ptr; michael@0: #endif michael@0: michael@0: michael@0: /* michael@0: * Initialize destination --- called by jpeg_start_compress michael@0: * before any data is actually written. michael@0: */ michael@0: michael@0: METHODDEF(void) michael@0: init_destination (j_compress_ptr cinfo) michael@0: { michael@0: my_dest_ptr dest = (my_dest_ptr) cinfo->dest; michael@0: michael@0: /* Allocate the output buffer --- it will be released when done with image */ michael@0: dest->buffer = (JOCTET *) michael@0: (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, michael@0: OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); michael@0: michael@0: dest->pub.next_output_byte = dest->buffer; michael@0: dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; michael@0: } michael@0: michael@0: #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) michael@0: METHODDEF(void) michael@0: init_mem_destination (j_compress_ptr cinfo) michael@0: { michael@0: /* no work necessary here */ michael@0: } michael@0: #endif michael@0: michael@0: michael@0: /* michael@0: * Empty the output buffer --- called whenever buffer fills up. michael@0: * michael@0: * In typical applications, this should write the entire output buffer michael@0: * (ignoring the current state of next_output_byte & free_in_buffer), michael@0: * reset the pointer & count to the start of the buffer, and return TRUE michael@0: * indicating that the buffer has been dumped. michael@0: * michael@0: * In applications that need to be able to suspend compression due to output michael@0: * overrun, a FALSE return indicates that the buffer cannot be emptied now. michael@0: * In this situation, the compressor will return to its caller (possibly with michael@0: * an indication that it has not accepted all the supplied scanlines). The michael@0: * application should resume compression after it has made more room in the michael@0: * output buffer. Note that there are substantial restrictions on the use of michael@0: * suspension --- see the documentation. michael@0: * michael@0: * When suspending, the compressor will back up to a convenient restart point michael@0: * (typically the start of the current MCU). next_output_byte & free_in_buffer michael@0: * indicate where the restart point will be if the current call returns FALSE. michael@0: * Data beyond this point will be regenerated after resumption, so do not michael@0: * write it out when emptying the buffer externally. michael@0: */ michael@0: michael@0: METHODDEF(boolean) michael@0: empty_output_buffer (j_compress_ptr cinfo) michael@0: { michael@0: my_dest_ptr dest = (my_dest_ptr) cinfo->dest; michael@0: michael@0: if (JFWRITE(dest->outfile, dest->buffer, OUTPUT_BUF_SIZE) != michael@0: (size_t) OUTPUT_BUF_SIZE) michael@0: ERREXIT(cinfo, JERR_FILE_WRITE); michael@0: michael@0: dest->pub.next_output_byte = dest->buffer; michael@0: dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; michael@0: michael@0: return TRUE; michael@0: } michael@0: michael@0: #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) michael@0: METHODDEF(boolean) michael@0: empty_mem_output_buffer (j_compress_ptr cinfo) michael@0: { michael@0: size_t nextsize; michael@0: JOCTET * nextbuffer; michael@0: my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest; michael@0: michael@0: /* Try to allocate new buffer with double size */ michael@0: nextsize = dest->bufsize * 2; michael@0: nextbuffer = (JOCTET *) malloc(nextsize); michael@0: michael@0: if (nextbuffer == NULL) michael@0: ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); michael@0: michael@0: MEMCOPY(nextbuffer, dest->buffer, dest->bufsize); michael@0: michael@0: if (dest->newbuffer != NULL) michael@0: free(dest->newbuffer); michael@0: michael@0: dest->newbuffer = nextbuffer; michael@0: michael@0: dest->pub.next_output_byte = nextbuffer + dest->bufsize; michael@0: dest->pub.free_in_buffer = dest->bufsize; michael@0: michael@0: dest->buffer = nextbuffer; michael@0: dest->bufsize = nextsize; michael@0: michael@0: return TRUE; michael@0: } michael@0: #endif michael@0: michael@0: michael@0: /* michael@0: * Terminate destination --- called by jpeg_finish_compress michael@0: * after all data has been written. Usually needs to flush buffer. michael@0: * michael@0: * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding michael@0: * application must deal with any cleanup that should happen even michael@0: * for error exit. michael@0: */ michael@0: michael@0: METHODDEF(void) michael@0: term_destination (j_compress_ptr cinfo) michael@0: { michael@0: my_dest_ptr dest = (my_dest_ptr) cinfo->dest; michael@0: size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; michael@0: michael@0: /* Write any data remaining in the buffer */ michael@0: if (datacount > 0) { michael@0: if (JFWRITE(dest->outfile, dest->buffer, datacount) != datacount) michael@0: ERREXIT(cinfo, JERR_FILE_WRITE); michael@0: } michael@0: fflush(dest->outfile); michael@0: /* Make sure we wrote the output file OK */ michael@0: if (ferror(dest->outfile)) michael@0: ERREXIT(cinfo, JERR_FILE_WRITE); michael@0: } michael@0: michael@0: #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) michael@0: METHODDEF(void) michael@0: term_mem_destination (j_compress_ptr cinfo) michael@0: { michael@0: my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest; michael@0: michael@0: *dest->outbuffer = dest->buffer; michael@0: *dest->outsize = (unsigned long)(dest->bufsize - dest->pub.free_in_buffer); michael@0: } michael@0: #endif michael@0: michael@0: michael@0: /* michael@0: * Prepare for output to a stdio stream. michael@0: * The caller must have already opened the stream, and is responsible michael@0: * for closing it after finishing compression. michael@0: */ michael@0: michael@0: GLOBAL(void) michael@0: jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile) michael@0: { michael@0: my_dest_ptr dest; michael@0: michael@0: /* The destination object is made permanent so that multiple JPEG images michael@0: * can be written to the same file without re-executing jpeg_stdio_dest. michael@0: * This makes it dangerous to use this manager and a different destination michael@0: * manager serially with the same JPEG object, because their private object michael@0: * sizes may be different. Caveat programmer. michael@0: */ michael@0: if (cinfo->dest == NULL) { /* first time for this JPEG object? */ michael@0: cinfo->dest = (struct jpeg_destination_mgr *) michael@0: (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, michael@0: SIZEOF(my_destination_mgr)); michael@0: } michael@0: michael@0: dest = (my_dest_ptr) cinfo->dest; michael@0: dest->pub.init_destination = init_destination; michael@0: dest->pub.empty_output_buffer = empty_output_buffer; michael@0: dest->pub.term_destination = term_destination; michael@0: dest->outfile = outfile; michael@0: } michael@0: michael@0: michael@0: #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) michael@0: /* michael@0: * Prepare for output to a memory buffer. michael@0: * The caller may supply an own initial buffer with appropriate size. michael@0: * Otherwise, or when the actual data output exceeds the given size, michael@0: * the library adapts the buffer size as necessary. michael@0: * The standard library functions malloc/free are used for allocating michael@0: * larger memory, so the buffer is available to the application after michael@0: * finishing compression, and then the application is responsible for michael@0: * freeing the requested memory. michael@0: */ michael@0: michael@0: GLOBAL(void) michael@0: jpeg_mem_dest (j_compress_ptr cinfo, michael@0: unsigned char ** outbuffer, unsigned long * outsize) michael@0: { michael@0: my_mem_dest_ptr dest; michael@0: michael@0: if (outbuffer == NULL || outsize == NULL) /* sanity check */ michael@0: ERREXIT(cinfo, JERR_BUFFER_SIZE); michael@0: michael@0: /* The destination object is made permanent so that multiple JPEG images michael@0: * can be written to the same buffer without re-executing jpeg_mem_dest. michael@0: */ michael@0: if (cinfo->dest == NULL) { /* first time for this JPEG object? */ michael@0: cinfo->dest = (struct jpeg_destination_mgr *) michael@0: (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, michael@0: SIZEOF(my_mem_destination_mgr)); michael@0: } michael@0: michael@0: dest = (my_mem_dest_ptr) cinfo->dest; michael@0: dest->pub.init_destination = init_mem_destination; michael@0: dest->pub.empty_output_buffer = empty_mem_output_buffer; michael@0: dest->pub.term_destination = term_mem_destination; michael@0: dest->outbuffer = outbuffer; michael@0: dest->outsize = outsize; michael@0: dest->newbuffer = NULL; michael@0: michael@0: if (*outbuffer == NULL || *outsize == 0) { michael@0: /* Allocate initial buffer */ michael@0: dest->newbuffer = *outbuffer = (unsigned char *) malloc(OUTPUT_BUF_SIZE); michael@0: if (dest->newbuffer == NULL) michael@0: ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); michael@0: *outsize = OUTPUT_BUF_SIZE; michael@0: } michael@0: michael@0: dest->pub.next_output_byte = dest->buffer = *outbuffer; michael@0: dest->pub.free_in_buffer = dest->bufsize = *outsize; michael@0: } michael@0: #endif