//////////////////////////////////////////////////////////////////// // ImageJPG.cpp // // Copyright 2007 cDc@seacave // Distributed under the Boost Software License, Version 1.0 // (See http://www.boost.org/LICENSE_1_0.txt) #include "Common.h" #ifdef _IMAGE_JPG #include "ImageJPG.h" #ifdef _MSC_VER #define XMD_H // prevent redefinition of INT32 #undef FAR // prevent FAR redefinition #endif extern "C" { #include } #include using namespace SEACAVE; // D E F I N E S /////////////////////////////////////////////////// #define JPG_BUFFER_SIZE (16*1024) struct JpegErrorMgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; struct JpegSource { struct jpeg_source_mgr pub; ISTREAM* pStream; JOCTET* buffer; }; struct JpegState { jpeg_decompress_struct cinfo; // IJG JPEG codec structure JpegErrorMgr jerr;// error processing manager state JpegSource source;// memory buffer source }; // F U N C T I O N S /////////////////////////////////////////////// METHODDEF(void) stub(j_decompress_ptr cinfo) { JpegSource* source = (JpegSource*)cinfo->src; source->pStream->setPos(0); } METHODDEF(boolean) fill_input_buffer(j_decompress_ptr cinfo) { JpegSource* source = (JpegSource*)cinfo->src; const size_t size = source->pStream->read(source->buffer, JPG_BUFFER_SIZE); if (size == STREAM_ERROR || size == 0) return FALSE; source->pub.next_input_byte = source->buffer; source->pub.bytes_in_buffer = size; return TRUE; } METHODDEF(void) skip_input_data(j_decompress_ptr cinfo, long num_bytes) { JpegSource* source = (JpegSource*)cinfo->src; if (num_bytes > (long)source->pub.bytes_in_buffer) { // We need to skip more data than we have in the buffer. source->pStream->setPos(source->pStream->getPos() + (num_bytes - source->pub.bytes_in_buffer)); source->pub.next_input_byte += source->pub.bytes_in_buffer; source->pub.bytes_in_buffer = 0; } else { // Skip portion of the buffer source->pub.bytes_in_buffer -= num_bytes; source->pub.next_input_byte += num_bytes; } } METHODDEF(void) error_exit(j_common_ptr cinfo) { JpegErrorMgr* err_mgr = (JpegErrorMgr*)(cinfo->err); /* Return control to the setjmp point */ longjmp( err_mgr->setjmp_buffer, 1 ); } // S T R U C T S /////////////////////////////////////////////////// CImageJPG::CImageJPG() : m_state(NULL) { } // Constructor CImageJPG::~CImageJPG() { //clean up Close(); } // Destructor /*----------------------------------------------------------------*/ void CImageJPG::Close() { if (m_state) { JpegState* state = (JpegState*)m_state; jpeg_destroy_decompress( &state->cinfo ); delete state; m_state = NULL; } m_width = m_height = 0; CImage::Close(); } /*----------------------------------------------------------------*/ HRESULT CImageJPG::ReadHeader() { JpegState* state = new JpegState; m_state = state; state->cinfo.err = jpeg_std_error(&state->jerr.pub); state->jerr.pub.error_exit = error_exit; if (setjmp(state->jerr.setjmp_buffer ) == 0) { jpeg_create_decompress( &state->cinfo ); // Prepare for suspending reader state->source.pub.init_source = stub; state->source.pub.fill_input_buffer = fill_input_buffer; state->source.pub.skip_input_data = skip_input_data; state->source.pub.resync_to_restart = jpeg_resync_to_restart; state->source.pub.term_source = stub; state->source.pub.bytes_in_buffer = 0;// forces fill_input_buffer on first read state->source.pub.next_input_byte = NULL; state->source.pStream = (IOSTREAM*)m_pStream; state->source.buffer = (JOCTET*)(*state->cinfo.mem->alloc_small)((j_common_ptr)&state->cinfo, JPOOL_PERMANENT, JPG_BUFFER_SIZE * sizeof(JOCTET)); state->cinfo.src = &state->source.pub; jpeg_read_header(&state->cinfo, TRUE); m_dataWidth = m_width = state->cinfo.image_width; m_dataHeight= m_height = state->cinfo.image_height; m_numLevels = 0; m_level = 0; m_stride = state->cinfo.num_components; m_lineWidth = m_width * m_stride; switch (m_stride) { case 1: m_format = PF_GRAY8; state->cinfo.out_color_space = JCS_GRAYSCALE; state->cinfo.out_color_components = 1; break; case 3: m_format = PF_B8G8R8; state->cinfo.out_color_space = JCS_RGB; state->cinfo.out_color_components = 3; break; default: LOG(LT_IMAGE, "error: unsupported JPG image"); return _INVALIDFILE; } return _OK; } Close(); return _FAIL; } // ReadHeader /*----------------------------------------------------------------*/ HRESULT CImageJPG::ReadData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth) { JpegState* state = (JpegState*)m_state; if (state && m_width && m_height) { jpeg_decompress_struct* cinfo = &state->cinfo; JpegErrorMgr* jerr = &state->jerr; if (setjmp(jerr->setjmp_buffer) == 0) { jpeg_start_decompress(cinfo); // read data if (dataFormat == m_format && nStride == m_stride) { // read image directly to the data buffer JSAMPLE* buffer[1] = {(JSAMPLE*)pData}; uint8_t*& data = (uint8_t*&)buffer[0]; for (Size j=0; jmem->alloc_sarray)((j_common_ptr)cinfo, JPOOL_IMAGE, m_lineWidth, 1); uint8_t* dst = (uint8_t*)pData; uint8_t* src = (uint8_t*)buffer[0]; for (Size j=0; j