// ========================================================== // JPEG2000 J2K codestream Loader and Writer // // Design and implementation by // - Hervé Drolon (drolon@infonie.fr) // // This file is part of FreeImage 3 // // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER // THIS DISCLAIMER. // // Use at your own risk! // ========================================================== #include "FreeImage.h" #include "Utilities.h" #include "../LibOpenJPEG/openjpeg.h" // ========================================================== // Plugin Interface // ========================================================== static int s_format_id; // ========================================================== // Helper functions (see J2KHelper.cpp) // ========================================================== FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image); opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters); // ========================================================== // Internal functions // ========================================================== /** OpenJPEG Error callback */ static void j2k_error_callback(const char *msg, void *client_data) { FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); } /** OpenJPEG Warning callback */ static void j2k_warning_callback(const char *msg, void *client_data) { FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg); } // ========================================================== // Plugin Implementation // ========================================================== static const char * DLL_CALLCONV Format() { return "J2K"; } static const char * DLL_CALLCONV Description() { return "JPEG-2000 codestream"; } static const char * DLL_CALLCONV Extension() { return "j2k,j2c"; } static const char * DLL_CALLCONV RegExpr() { return NULL; } static const char * DLL_CALLCONV MimeType() { return "image/j2k"; } static BOOL DLL_CALLCONV Validate(FreeImageIO *io, fi_handle handle) { BYTE jpc_signature[] = { 0xFF, 0x4F }; BYTE signature[2] = { 0, 0 }; long tell = io->tell_proc(handle); io->read_proc(signature, 1, sizeof(jpc_signature), handle); io->seek_proc(handle, tell, SEEK_SET); return (memcmp(jpc_signature, signature, sizeof(jpc_signature)) == 0); } static BOOL DLL_CALLCONV SupportsExportDepth(int depth) { return ( (depth == 8) || (depth == 24) || (depth == 32) ); } static BOOL DLL_CALLCONV SupportsExportType(FREE_IMAGE_TYPE type) { return ( (type == FIT_BITMAP) || (type == FIT_UINT16) || (type == FIT_RGB16) || (type == FIT_RGBA16) ); } // ---------------------------------------------------------- static void * DLL_CALLCONV Open(FreeImageIO *io, fi_handle handle, BOOL read) { return NULL; } static void DLL_CALLCONV Close(FreeImageIO *io, fi_handle handle, void *data) { } // ---------------------------------------------------------- static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (handle) { opj_dparameters_t parameters; // decompression parameters opj_event_mgr_t event_mgr; // event manager opj_image_t *image = NULL; // decoded image BYTE *src = NULL; long file_length; opj_dinfo_t* dinfo = NULL; // handle to a decompressor opj_cio_t *cio = NULL; FIBITMAP *dib = NULL; // check the file format if (!Validate(io, handle)) { return NULL; } // configure the event callbacks memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); event_mgr.error_handler = j2k_error_callback; event_mgr.warning_handler = j2k_warning_callback; event_mgr.info_handler = NULL; // set decoding parameters to default values opj_set_default_decoder_parameters(¶meters); try { // read the input file and put it in memory long start_pos = io->tell_proc(handle); io->seek_proc(handle, 0, SEEK_END); file_length = io->tell_proc(handle) - start_pos; io->seek_proc(handle, start_pos, SEEK_SET); src = (BYTE*)malloc(file_length * sizeof(BYTE)); if (!src) { throw FI_MSG_ERROR_MEMORY; } if(io->read_proc(src, 1, file_length, handle) < 1) { throw "Error while reading input stream"; } // decode the JPEG-2000 codestream // get a decoder handle dinfo = opj_create_decompress(CODEC_J2K); // catch events using our callbacks opj_set_event_mgr((opj_common_ptr)dinfo, &event_mgr, NULL); // setup the decoder decoding parameters using user parameters opj_setup_decoder(dinfo, ¶meters); // open a byte stream cio = opj_cio_open((opj_common_ptr)dinfo, src, file_length); // decode the stream and fill the image structure image = opj_decode(dinfo, cio); if (!image) { throw "Failed to decode image!\n"; } // close the byte stream opj_cio_close(cio); cio = NULL; // free the memory containing the code-stream free(src); src = NULL; // free the codec context opj_destroy_decompress(dinfo); // create output image dib = J2KImageToFIBITMAP(s_format_id, image); if (!dib) throw "Failed to import JPEG2000 image"; // free image data structure opj_image_destroy(image); return dib; } catch (const char *text) { if(src) free(src); if(dib) FreeImage_Unload(dib); // free remaining structures opj_destroy_decompress(dinfo); opj_image_destroy(image); // close the byte stream if(cio) opj_cio_close(cio); FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; } static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { if ((dib) && (handle)) { BOOL bSuccess; opj_cparameters_t parameters; // compression parameters opj_event_mgr_t event_mgr; // event manager opj_image_t *image = NULL; // image to encode opj_cinfo_t* cinfo = NULL; // codec context opj_cio_t *cio = NULL; // memory byte stream // configure the event callbacks memset(&event_mgr, 0, sizeof(opj_event_mgr_t)); event_mgr.error_handler = j2k_error_callback; event_mgr.warning_handler = j2k_warning_callback; event_mgr.info_handler = NULL; // set encoding parameters to default values opj_set_default_encoder_parameters(¶meters); parameters.tcp_numlayers = 0; // if no rate entered, apply a 16:1 rate by default if(flags == J2K_DEFAULT) { parameters.tcp_rates[0] = (float)16; } else { // for now, the flags parameter is only used to specify the rate parameters.tcp_rates[0] = (float)flags; } parameters.tcp_numlayers++; parameters.cp_disto_alloc = 1; try { // convert the dib to a OpenJPEG image image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters); if (!image) return FALSE; // decide if MCT should be used parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0; // encode the destination image // get a J2K compressor handle cinfo = opj_create_compress(CODEC_J2K); // catch events using our callbacks opj_set_event_mgr((opj_common_ptr)cinfo, &event_mgr, NULL); // setup the encoder parameters using the current image and using user parameters opj_setup_encoder(cinfo, ¶meters, image); // open a byte stream for writing, allocate memory for all tiles cio = opj_cio_open((opj_common_ptr)cinfo, NULL, 0); // encode the image bSuccess = opj_encode(cinfo, cio, image, NULL/*parameters.index*/); if (!bSuccess) { throw "Failed to encode image"; } int codestream_length = cio_tell(cio); // write the buffer to user's IO handle io->write_proc(cio->buffer, 1, codestream_length, handle); // close and free the byte stream opj_cio_close(cio); // free remaining compression structures opj_destroy_compress(cinfo); // free image data opj_image_destroy(image); return TRUE; } catch (const char *text) { if(cio) opj_cio_close(cio); if(cinfo) opj_destroy_compress(cinfo); if(image) opj_image_destroy(image); FreeImage_OutputMessageProc(s_format_id, text); return FALSE; } } return FALSE; } // ========================================================== // Init // ========================================================== void DLL_CALLCONV InitJ2K(Plugin *plugin, int format_id) { s_format_id = format_id; plugin->format_proc = Format; plugin->description_proc = Description; plugin->extension_proc = Extension; plugin->regexpr_proc = RegExpr; plugin->open_proc = Open; plugin->close_proc = Close; plugin->pagecount_proc = NULL; plugin->pagecapability_proc = NULL; plugin->load_proc = Load; plugin->save_proc = Save; plugin->validate_proc = Validate; plugin->mime_proc = MimeType; plugin->supports_export_bpp_proc = SupportsExportDepth; plugin->supports_export_type_proc = SupportsExportType; plugin->supports_icc_profiles_proc = NULL; }