123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- #define XMD_H
- #include "JPEGData.h"
- #include "MemStream.h"
- #include "ImageUtils.h"
- #include <setjmp.h>
- extern "C"
- {
- #include "jpeg/jpeglib.h"
- #include "jpeg/jerror.h"
- #include "jpeg/jpegint.h"
- }
- USING_NS_BF;
- // -----------------------------------------------------------------------
- // JPEG error handling
- // Will "longjmp" on error_exit.
- // -----------------------------------------------------------------------
- struct ErrorHandler
- {
- /** "subclass" of jpeg_error_mgr */
- struct jpeg_error_mgr errorMgr;
- jmp_buf setjmpBuffer;
- ErrorHandler( j_decompress_ptr cinfo )
- {
- Init( (j_common_ptr)cinfo );
- }
- ErrorHandler( j_compress_ptr cinfo )
- {
- Init( (j_common_ptr)cinfo );
- }
- void Init( j_common_ptr cinfo )
- {
- // setup the standard error handling.
- cinfo->err = jpeg_std_error( &errorMgr );
- // then hook up our error_exit function.
- errorMgr.error_exit = &ErrorHandler::OnErrorExit;
- }
- static void OnErrorExit( j_common_ptr cinfo )
- {
- // recover the pointer to "derived class" instance
- ErrorHandler* errorHandler = (ErrorHandler*)cinfo->err;
- // use the default error message output.
- (*cinfo->err->output_message)( cinfo );
- // return control to the setjmp point.
- longjmp( errorHandler->setjmpBuffer, 1 );
- }
- };
- struct JpegMemSource
- {
- JpegMemSource(JPEGData* buffer, j_decompress_ptr cinfo )
- {
- struct jpeg_source_mgr * src;
- if ( cinfo->src == NULL )
- {
- // Have the jpeg library allocate the source manager object so
- // that it is automatically deallocated by the decompressor.
- cinfo->src = (struct jpeg_source_mgr *)(*cinfo->mem->alloc_small)( (j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr) );
- }
- src = cinfo->src;
- src->init_source = &JpegMemSource::InitSource;
- src->fill_input_buffer = &JpegMemSource::FillInputBuffer;
- src->skip_input_data = &JpegMemSource::SkipInputData;
- src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
- src->term_source = &JpegMemSource::TermSource;
- src->bytes_in_buffer = (size_t) buffer->mSrcDataLen;
- src->next_input_byte = (JOCTET *) buffer->mSrcData;
- }
- ~JpegMemSource()
- {
- // The setjmp/longjmp action in ErrorHandler can actually
- // completely skip the destruction of this C++ data source wrapper,
- // but that is OK so long as there is no actual cleanup to do.
- }
- static void InitSource( j_decompress_ptr cinfo )
- {
- /* no work necessary here */
- }
- static boolean FillInputBuffer( j_decompress_ptr cinfo )
- {
- static JOCTET mybuffer[4];
- /* The whole JPEG data is expected to reside in the supplied memory
- * buffer, so any request for more data beyond the given buffer size
- * is treated as an error.
- */
- WARNMS(cinfo, JWRN_JPEG_EOF);
- /* Insert a fake EOI marker */
- mybuffer[0] = (JOCTET) 0xFF;
- mybuffer[1] = (JOCTET) JPEG_EOI;
- cinfo->src->next_input_byte = mybuffer;
- cinfo->src->bytes_in_buffer = 2;
- return TRUE;
- }
- static void SkipInputData( j_decompress_ptr cinfo, long num_bytes )
- {
- struct jpeg_source_mgr * src = cinfo->src;
- /* Just a dumb implementation for now. Could use fseek() except
- * it doesn't work on pipes. Not clear that being smart is worth
- * any trouble anyway --- large skips are infrequent.
- */
- if (num_bytes > 0) {
- while (num_bytes > (long) src->bytes_in_buffer) {
- num_bytes -= (long) src->bytes_in_buffer;
- (void) (*src->fill_input_buffer) (cinfo);
- /* note we assume that fill_input_buffer will never return FALSE,
- * so suspension need not be handled.
- */
- }
- src->next_input_byte += (size_t) num_bytes;
- src->bytes_in_buffer -= (size_t) num_bytes;
- }
- }
- static void TermSource( j_decompress_ptr cinfo )
- {
- /* no work necessary here */
- }
- };
- #define JPEGMEMDEST_BLOCK_SIZE 16384
- struct JpegMemDest : jpeg_destination_mgr
- {
- Array<uint8> mData;
- JpegMemDest(JPEGData* buffer, j_compress_ptr cinfo)
- {
- if (cinfo->dest == NULL)
- {
- cinfo->dest = this;
- }
- init_destination = &InitDestination;
- empty_output_buffer = &EmptyOutputBuffer;
- term_destination = &TermDestination;
- }
- ~JpegMemDest()
- {
- }
- static void InitDestination(j_compress_ptr cinfo)
- {
- auto self = (JpegMemDest*)cinfo->dest;
- self->mData.Resize(JPEGMEMDEST_BLOCK_SIZE);
- cinfo->dest->next_output_byte = &self->mData[0];
- cinfo->dest->free_in_buffer = self->mData.size();
- }
- static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
- {
- auto self = (JpegMemDest*)cinfo->dest;
- size_t oldsize = self->mData.size();
- self->mData.Resize(oldsize + JPEGMEMDEST_BLOCK_SIZE);
- cinfo->dest->next_output_byte = &self->mData[oldsize];
- cinfo->dest->free_in_buffer = self->mData.size() - oldsize;
- return true;
- }
- static void TermDestination(j_compress_ptr cinfo)
- {
- auto self = (JpegMemDest*)cinfo->dest;
- self->mData.Resize(self->mData.size() - cinfo->dest->free_in_buffer);
- }
- };
- bool JPEGData::ReadData()
- {
- jpeg_decompress_struct cinfo;
- ErrorHandler err( &cinfo );
- if ( setjmp( err.setjmpBuffer ) )
- {
- // ErrorHandler::OnErrorExit will longjmp back to here from
- // within the ReadImage call below.
- jpeg_destroy_decompress( &cinfo );
- return false;
- }
- jpeg_create_decompress( &cinfo );
- JpegMemSource(this, &cinfo );
- jpeg_read_header( &cinfo, TRUE );
- jpeg_start_decompress( &cinfo );
- mWidth = cinfo.output_width;
- mHeight = cinfo.output_height;
- mBits = new uint32[ mWidth * mHeight ];
- uint32* destPtr = mBits;
- // Have the jpeg library allocate a scan-line buffer as a
- // per-image resource so that it will be automatically deallocated
- // by jpeg_finish_decompress.
- int row_stride = cinfo.output_width * cinfo.output_components;
- unsigned char** scanline = (*cinfo.mem->alloc_sarray)( (j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1 );
- if ( cinfo.output_components == 1 )
- {
- while ( cinfo.output_scanline < cinfo.output_height )
- {
- jpeg_read_scanlines( &cinfo, scanline, 1 );
- uint8* p = *scanline;
- for ( JDIMENSION i = 0; i < cinfo.output_width; ++i )
- {
- int r = *p++;
- *destPtr++ = 0xFF000000 | (r << 16) | (r << 8) | (r);
- }
- }
- }
- else
- {
- while ( cinfo.output_scanline < cinfo.output_height )
- {
- jpeg_read_scanlines(&cinfo, scanline, 1 );
- uint8* p = *scanline;
- for ( JDIMENSION i = 0; i < cinfo.output_width; ++i )
- {
- int b = *p++;
- int g = *p++;
- int r = *p++;
- *destPtr++ = 0xFF000000 | (r << 16) | (g << 8) | (b);
- }
- }
- }
- jpeg_finish_decompress( &cinfo );
- jpeg_destroy_decompress( &cinfo );
- return true;
- }
- void JPEGData::Compress(int quality)
- {
- jpeg_compress_struct cinfo;
- /* Now we can initialize the JPEG compression object. */
- jpeg_create_compress(&cinfo);
- ErrorHandler err(&cinfo);
- if (setjmp(err.setjmpBuffer))
- {
- // ErrorHandler::OnErrorExit will longjmp back to here from
- // within the ReadImage call below.
- jpeg_destroy_compress(&cinfo);
- return;
- }
- JSAMPROW row_pointer[1];
- JpegMemDest jpegMemDest(this, &cinfo);
- //jpeg_stdio_dest(&cinfo, outfile);
- /* Step 3: set parameters for compression */
- /* First we supply a description of the input image.
- * Four fields of the cinfo struct must be filled in:
- */
- cinfo.image_width = mWidth; /* image width and height, in pixels */
- cinfo.image_height = mHeight;
- cinfo.input_components = 3; /* # of color components per pixel */
- cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
- /* Now use the library's routine to set default compression parameters.
- * (You must set at least cinfo.in_color_space before calling this,
- * since the defaults depend on the source color space.)
- */
- jpeg_set_defaults(&cinfo);
- /* Now you can set any non-default parameters you wish to.
- * Here we just illustrate the use of quality (quantization table) scaling:
- */
- jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
- /* Step 4: Start compressor */
- /* TRUE ensures that we will write a complete interchange-JPEG file.
- * Pass TRUE unless you are very sure of what you're doing.
- */
- jpeg_start_compress(&cinfo, TRUE);
- /* Step 5: while (scan lines remain to be written) */
- /* jpeg_write_scanlines(...); */
- /* Here we use the library's state variable cinfo.next_scanline as the
- * loop counter, so that we don't have to keep track ourselves.
- * To keep things simple, we pass one scanline per call; you can pass
- * more if you wish, though.
- */
- int row_stride = mWidth * 3; /* JSAMPLEs per row in image_buffer */
- uint8* line = new uint8[mWidth * 3];
- row_pointer[0] = (JSAMPROW)line;
- while (cinfo.next_scanline < cinfo.image_height)
- {
- uint8* src = (uint8*)&mBits[cinfo.next_scanline * mWidth];
- uint8* dest = line;
- for (int x = 0; x < mWidth; x++)
- {
- *(dest++) = src[2];
- *(dest++) = src[1];
- *(dest++) = src[0];
- src += 4;
- }
- (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
- }
- delete line;
- /* Step 6: Finish compression */
- jpeg_finish_compress(&cinfo);
- /* After finish_compress, we can close the output file. */
- //fclose(outfile);
- /* Step 7: release JPEG compression object */
- /* This is an important step since it will release a good deal of memory. */
- jpeg_destroy_compress(&cinfo);
- mSrcDataLen = (int)jpegMemDest.mData.size();
- mSrcData = new uint8[mSrcDataLen];
- memcpy(mSrcData, &jpegMemDest.mData[0], mSrcDataLen);
- }
|