You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

281 lines
7.3 KiB

////////////////////////////////////////////////////////////////////
// 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 <jpeglib.h>
}
#include <setjmp.h>
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; j<m_height; ++j, data+=lineWidth)
jpeg_read_scanlines(cinfo, buffer, 1);
} else {
// read image to a buffer and convert it
JSAMPARRAY buffer = (*cinfo->mem->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<m_height; ++j, dst+=lineWidth) {
jpeg_read_scanlines(cinfo, buffer, 1);
if (!FilterFormat(dst, dataFormat, nStride, src, m_format, m_stride, m_width))
return _FAIL;
}
}
jpeg_finish_decompress(cinfo);
return _OK;
}
}
Close();
return _FAIL;
} // Read
/*----------------------------------------------------------------*/
HRESULT CImageJPG::WriteHeader(PIXELFORMAT imageFormat, Size width, Size height, BYTE numLevels)
{
//TODO: to implement the JPG encoder
return _OK;
} // WriteHeader
/*----------------------------------------------------------------*/
HRESULT CImageJPG::WriteData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth)
{
//TODO: to implement the JPG encoder
//const int quality = 100;
//struct jpeg_compress_struct cinfo;
//struct jpeg_error_mgr jerr;
///* More stuff */
//FILE * outfile; /* target file */
//JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
//int row_stride; /* physical row width in image buffer */
//cinfo.err = jpeg_std_error(&jerr);
//jpeg_create_compress(&cinfo);
//if ((outfile = fopen(filename.c_str(), "wb")) == NULL) {
// fprintf(stderr, "can't open %s\n", filename.c_str());
// exit(1);
//}
//jpeg_stdio_dest(&cinfo, outfile);
//cinfo.image_width = width;
//cinfo.image_height = height;
//cinfo.input_components = 3;
//cinfo.in_color_space = JCS_RGB;
//jpeg_set_defaults(&cinfo);
//jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
//jpeg_start_compress(&cinfo, TRUE);
//row_stride = width * 3; /* JSAMPLEs per row in image_buffer */
//while (cinfo.next_scanline < cinfo.image_height) {
// if (flip)
// row_pointer[0] = (JSAMPROW)& buffer[(cinfo.image_height - 1 - cinfo.next_scanline) * row_stride];
// else
// row_pointer[0] = (JSAMPROW)& buffer[cinfo.next_scanline * row_stride];
// (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
//}
//jpeg_finish_compress(&cinfo);
//fclose(outfile);
//jpeg_destroy_compress(&cinfo);
return _OK;
} // WriteData
/*----------------------------------------------------------------*/
#endif // _IMAGE_JPG