//======================================================================== // // JPEG2000Stream.cc // // A JPX stream decoder using OpenJPEG // // Copyright 2008-2010, 2012 Albert Astals Cid // Copyright 2011 Daniel Glöckner // Copyright 2013 Adrian Johnson // // Licensed under GPLv2 or later // //======================================================================== #include "JPEG2000Stream.h" #include "config.h" JPXStream::JPXStream(Stream *strA) : FilterStream(strA) { inited = gFalse; image = NULL; npixels = 0; ncomps = 0; } JPXStream::~JPXStream() { delete str; close(); } void JPXStream::reset() { counter = 0; ccounter = 0; } void JPXStream::close() { if (image != NULL) { opj_image_destroy(image); image = NULL; npixels = 0; } } Goffset JPXStream::getPos() { return counter * ncomps + ccounter; } int JPXStream::getChars(int nChars, Guchar *buffer) { for (int i = 0; i < nChars; ++i) { const int c = doGetChar(); if (likely(c != EOF)) buffer[i] = c; else return i; } return nChars; } int JPXStream::getChar() { return doGetChar(); } #define BUFFER_INITIAL_SIZE 4096 void JPXStream::init() { Object oLen; if (getDict()) getDict()->lookup("Length", &oLen); int bufSize = BUFFER_INITIAL_SIZE; if (oLen.isInt()) bufSize = oLen.getInt(); oLen.free(); int length = 0; unsigned char *buf = str->toUnsignedChars(&length, bufSize); init2(buf, length); free(buf); if (image) { npixels = image->comps[0].w * image->comps[0].h; ncomps = image->numcomps; for (int component = 0; component < ncomps; component++) { if (image->comps[component].data == NULL) { close(); break; } unsigned char *cdata = (unsigned char *)image->comps[component].data; int adjust = 0; if (image->comps[component].prec > 8) adjust = image->comps[component].prec - 8; int sgndcorr = 0; if (image->comps[component].sgnd) sgndcorr = 1 << (image->comps[0].prec - 1); for (int i = 0; i < npixels; i++) { int r = image->comps[component].data[i]; r += sgndcorr; if (adjust) { r = (r >> adjust)+((r >> (adjust-1))%2); if (unlikely(r > 255)) r = 255; } *(cdata++) = r; } } } else npixels = 0; counter = 0; ccounter = 0; inited = gTrue; } static void libopenjpeg_error_callback(const char *msg, void * /*client_data*/) { error(errSyntaxError, -1, "{0:s}", msg); } static void libopenjpeg_warning_callback(const char *msg, void * /*client_data*/) { error(errSyntaxWarning, -1, "{0:s}", msg); } // static void libopenjpeg_info_callback(const char *msg, void * /*client_data*/) { // fprintf(stderr, "INFO: %s\n", msg); //} typedef struct JPXData_s { unsigned char *data; int size; int pos; } JPXData; static OPJ_SIZE_T jpxRead_callback(void * p_buffer, OPJ_SIZE_T p_nb_bytes, void * p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; int len; len = jpxData->size - jpxData->pos; if (len < 0) len = 0; if (len == 0) return (OPJ_SIZE_T)-1; /* End of file! */ if ((OPJ_SIZE_T)len > p_nb_bytes) len = p_nb_bytes; memcpy(p_buffer, jpxData->data + jpxData->pos, len); jpxData->pos += len; return len; } static OPJ_OFF_T jpxSkip_callback(OPJ_OFF_T skip, void * p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; if (skip > jpxData->size - jpxData->pos) skip = jpxData->size - jpxData->pos; jpxData->pos += skip; return jpxData->pos; } static OPJ_BOOL jpxSeek_callback(OPJ_OFF_T seek_pos, void * p_user_data) { JPXData *jpxData = (JPXData *)p_user_data; if (seek_pos > jpxData->size) return OPJ_FALSE; jpxData->pos = seek_pos; return OPJ_TRUE; } void JPXStream::init2(unsigned char *buf, int bufLen) { JPXData jpxData; jpxData.data = buf; jpxData.pos = 0; jpxData.size = bufLen; opj_stream_t *stream; stream = opj_stream_default_create(OPJ_TRUE); opj_stream_set_user_data (stream, &jpxData, NULL); opj_stream_set_read_function(stream, jpxRead_callback); opj_stream_set_skip_function(stream, jpxSkip_callback); opj_stream_set_seek_function(stream, jpxSeek_callback); /* Set the length to avoid an assert */ opj_stream_set_user_data_length(stream, bufLen); opj_codec_t *decoder; /* Use default decompression parameters */ opj_dparameters_t parameters; opj_set_default_decoder_parameters(¶meters); #ifdef WITH_OPENJPEG_IGNORE_PCLR_CMAP_CDEF_FLAG parameters.flags = OPJ_DPARAMETERS_IGNORE_PCLR_CMAP_CDEF_FLAG; #endif /* Get the decoder handle of the format */ if (bufLen < 2 || (buf[0] == 0xFF && buf[1] == 0x4F)) // Is it possible that bufLen < 2? decoder = opj_create_decompress(OPJ_CODEC_J2K); else decoder = opj_create_decompress(OPJ_CODEC_JP2); if (decoder == NULL) { error(errSyntaxWarning, -1, "Unable to create decoder"); goto error; } /* Catch events using our callbacks */ opj_set_warning_handler(decoder, libopenjpeg_warning_callback, NULL); opj_set_error_handler(decoder, libopenjpeg_error_callback, NULL); // opj_set_info_handler(decoder, libopenjpeg_info_callback, NULL); /* Setup the decoder decoding parameters */ if (!opj_setup_decoder(decoder, ¶meters)) { error(errSyntaxWarning, -1, "Unable to set decoder parameters"); goto error; } /* Decode the stream and fill the image structure */ image = NULL; if (!opj_read_header(stream, decoder, &image)) { error(errSyntaxWarning, -1, "Unable to read header"); goto error; } /* Get the decoded image */ if (!(opj_decode(decoder, stream, image))) { error(errSyntaxWarning, -1, "Unable to decode image"); goto error; } error: opj_destroy_codec(decoder); opj_stream_destroy(stream); } int JPXStream::lookChar() { return doLookChar(); } GooString *JPXStream::getPSFilter(int psLevel, const char *indent) { return NULL; } GBool JPXStream::isBinary(GBool last) { return str->isBinary(gTrue); } void JPXStream::getImageParams(int *bitsPerComponent, StreamColorSpaceMode *csMode) { if (inited == gFalse) init(); *bitsPerComponent = 8; if (image && image->numcomps == 3) *csMode = streamCSDeviceRGB; else *csMode = streamCSDeviceGray; }