| // ========================================================== |
| // JPEG2000 JP2 file format 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 "third_party/openjpeg2/src/lib/openjp2/openjpeg.h" |
| #include "J2KHelper.h" |
| |
| // ========================================================== |
| // Plugin Interface |
| // ========================================================== |
| |
| static int s_format_id; |
| |
| // ========================================================== |
| // Internal functions |
| // ========================================================== |
| |
| /** |
| OpenJPEG Error callback |
| */ |
| static void jp2_error_callback(const char *msg, void *client_data) { |
| FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg); |
| } |
| /** |
| OpenJPEG Warning callback |
| */ |
| static void jp2_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 "JP2"; |
| } |
| |
| static const char * DLL_CALLCONV |
| Description() { |
| return "JPEG-2000 File Format"; |
| } |
| |
| static const char * DLL_CALLCONV |
| Extension() { |
| return "jp2"; |
| } |
| |
| static const char * DLL_CALLCONV |
| RegExpr() { |
| return NULL; |
| } |
| |
| static const char * DLL_CALLCONV |
| MimeType() { |
| return "image/jp2"; |
| } |
| |
| static BOOL DLL_CALLCONV |
| Validate(FreeImageIO *io, fi_handle handle) { |
| BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A }; |
| BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| |
| long tell = io->tell_proc(handle); |
| io->read_proc(signature, 1, sizeof(jp2_signature), handle); |
| io->seek_proc(handle, tell, SEEK_SET); |
| |
| return (memcmp(jp2_signature, signature, sizeof(jp2_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) { |
| // create the stream wrapper |
| J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read); |
| return fio; |
| } |
| |
| static void DLL_CALLCONV |
| Close(FreeImageIO *io, fi_handle handle, void *data) { |
| // destroy the stream wrapper |
| J2KFIO_t *fio = (J2KFIO_t*)data; |
| opj_freeimage_stream_destroy(fio); |
| } |
| |
| // ---------------------------------------------------------- |
| |
| static FIBITMAP * DLL_CALLCONV |
| Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
| J2KFIO_t *fio = (J2KFIO_t*)data; |
| if (handle && fio) { |
| opj_codec_t *d_codec = NULL; // handle to a decompressor |
| opj_dparameters_t parameters; // decompression parameters |
| opj_image_t *image = NULL; // decoded image |
| |
| FIBITMAP *dib = NULL; |
| |
| // check the file format |
| if(!Validate(io, handle)) { |
| return NULL; |
| } |
| |
| BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; |
| |
| // get the OpenJPEG stream |
| opj_stream_t *d_stream = fio->stream; |
| |
| // set decoding parameters to default values |
| opj_set_default_decoder_parameters(¶meters); |
| |
| try { |
| // decode the JPEG-2000 file |
| |
| // get a decoder handle |
| d_codec = opj_create_decompress(OPJ_CODEC_JP2); |
| |
| // configure the event callbacks |
| // catch events using our callbacks (no local context needed here) |
| opj_set_info_handler(d_codec, NULL, NULL); |
| opj_set_warning_handler(d_codec, jp2_warning_callback, NULL); |
| opj_set_error_handler(d_codec, jp2_error_callback, NULL); |
| |
| // setup the decoder decoding parameters using user parameters |
| if( !opj_setup_decoder(d_codec, ¶meters) ) { |
| throw "Failed to setup the decoder\n"; |
| } |
| |
| // read the main header of the codestream and if necessary the JP2 boxes |
| if( !opj_read_header(d_stream, d_codec, &image)) { |
| throw "Failed to read the header\n"; |
| } |
| |
| // --- header only mode |
| |
| if (header_only) { |
| // create output image |
| dib = J2KImageToFIBITMAP(s_format_id, image, header_only); |
| if(!dib) { |
| throw "Failed to import JPEG2000 image"; |
| } |
| // clean-up and return header data |
| opj_destroy_codec(d_codec); |
| opj_image_destroy(image); |
| return dib; |
| } |
| |
| // decode the stream and fill the image structure |
| if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) { |
| throw "Failed to decode image!\n"; |
| } |
| |
| // free the codec context |
| opj_destroy_codec(d_codec); |
| d_codec = NULL; |
| |
| // create output image |
| dib = J2KImageToFIBITMAP(s_format_id, image, header_only); |
| if(!dib) { |
| throw "Failed to import JPEG2000 image"; |
| } |
| |
| // free image data structure |
| opj_image_destroy(image); |
| |
| return dib; |
| |
| } catch (const char *text) { |
| if(dib) { |
| FreeImage_Unload(dib); |
| } |
| // free remaining structures |
| opj_destroy_codec(d_codec); |
| opj_image_destroy(image); |
| |
| 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) { |
| J2KFIO_t *fio = (J2KFIO_t*)data; |
| if (dib && handle && fio) { |
| BOOL bSuccess; |
| opj_codec_t *c_codec = NULL; // handle to a compressor |
| opj_cparameters_t parameters; // compression parameters |
| opj_image_t *image = NULL; // image to encode |
| |
| // get the OpenJPEG stream |
| opj_stream_t *c_stream = fio->stream; |
| |
| // set encoding parameters to default values |
| opj_set_default_encoder_parameters(¶meters); |
| |
| try { |
| parameters.tcp_numlayers = 0; |
| // if no rate entered, apply a 16:1 rate by default |
| if(flags == JP2_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 & 0x3FF); |
| } |
| parameters.tcp_numlayers++; |
| parameters.cp_disto_alloc = 1; |
| |
| // 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 JP2 compressor handle |
| c_codec = opj_create_compress(OPJ_CODEC_JP2); |
| |
| // configure the event callbacks |
| // catch events using our callbacks (no local context needed here) |
| opj_set_info_handler(c_codec, NULL, NULL); |
| opj_set_warning_handler(c_codec, jp2_warning_callback, NULL); |
| opj_set_error_handler(c_codec, jp2_error_callback, NULL); |
| |
| // setup the encoder parameters using the current image and using user parameters |
| opj_setup_encoder(c_codec, ¶meters, image); |
| |
| // encode the image |
| bSuccess = opj_start_compress(c_codec, image, c_stream); |
| if(bSuccess) { |
| bSuccess = bSuccess && opj_encode(c_codec, c_stream); |
| if(bSuccess) { |
| bSuccess = bSuccess && opj_end_compress(c_codec, c_stream); |
| } |
| } |
| if (!bSuccess) { |
| throw "Failed to encode image"; |
| } |
| |
| // free remaining compression structures |
| opj_destroy_codec(c_codec); |
| |
| // free image data |
| opj_image_destroy(image); |
| |
| return TRUE; |
| |
| } catch (const char *text) { |
| if(c_codec) opj_destroy_codec(c_codec); |
| if(image) opj_image_destroy(image); |
| FreeImage_OutputMessageProc(s_format_id, text); |
| return FALSE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| // ========================================================== |
| // Init |
| // ========================================================== |
| |
| void DLL_CALLCONV |
| InitJP2(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; |
| } |