1 // ==========================================================
2 // JPEG2000 JP2 file format Loader and Writer
3 //
4 // Design and implementation by
5 // - Herv� Drolon (drolon@infonie.fr)
6 //
7 // This file is part of FreeImage 3
8 //
9 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17 // THIS DISCLAIMER.
18 //
19 // Use at your own risk!
20 // ==========================================================
21
22 #include "FreeImage.h"
23 #include "Utilities.h"
24 #include "../LibOpenJPEG/openjpeg.h"
25 #include "J2KHelper.h"
26
27 // ==========================================================
28 // Plugin Interface
29 // ==========================================================
30
31 static int s_format_id;
32
33 // ==========================================================
34 // Internal functions
35 // ==========================================================
36
37 /**
38 OpenJPEG Error callback
39 */
jp2_error_callback(const char * msg,void * client_data)40 static void jp2_error_callback(const char *msg, void *client_data) {
41 FreeImage_OutputMessageProc(s_format_id, "Error: %s", msg);
42 }
43 /**
44 OpenJPEG Warning callback
45 */
jp2_warning_callback(const char * msg,void * client_data)46 static void jp2_warning_callback(const char *msg, void *client_data) {
47 FreeImage_OutputMessageProc(s_format_id, "Warning: %s", msg);
48 }
49
50 // ==========================================================
51 // Plugin Implementation
52 // ==========================================================
53
54 static const char * DLL_CALLCONV
Format()55 Format() {
56 return "JP2";
57 }
58
59 static const char * DLL_CALLCONV
Description()60 Description() {
61 return "JPEG-2000 File Format";
62 }
63
64 static const char * DLL_CALLCONV
Extension()65 Extension() {
66 return "jp2";
67 }
68
69 static const char * DLL_CALLCONV
RegExpr()70 RegExpr() {
71 return NULL;
72 }
73
74 static const char * DLL_CALLCONV
MimeType()75 MimeType() {
76 return "image/jp2";
77 }
78
79 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)80 Validate(FreeImageIO *io, fi_handle handle) {
81 BYTE jp2_signature[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
82 BYTE signature[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
83
84 long tell = io->tell_proc(handle);
85 io->read_proc(signature, 1, sizeof(jp2_signature), handle);
86 io->seek_proc(handle, tell, SEEK_SET);
87
88 return (memcmp(jp2_signature, signature, sizeof(jp2_signature)) == 0);
89 }
90
91 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)92 SupportsExportDepth(int depth) {
93 return (
94 (depth == 8) ||
95 (depth == 24) ||
96 (depth == 32)
97 );
98 }
99
100 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)101 SupportsExportType(FREE_IMAGE_TYPE type) {
102 return (
103 (type == FIT_BITMAP) ||
104 (type == FIT_UINT16) ||
105 (type == FIT_RGB16) ||
106 (type == FIT_RGBA16)
107 );
108 }
109
110 // ----------------------------------------------------------
111
112 static void * DLL_CALLCONV
Open(FreeImageIO * io,fi_handle handle,BOOL read)113 Open(FreeImageIO *io, fi_handle handle, BOOL read) {
114 // create the stream wrapper
115 J2KFIO_t *fio = opj_freeimage_stream_create(io, handle, read);
116 return fio;
117 }
118
119 static void DLL_CALLCONV
Close(FreeImageIO * io,fi_handle handle,void * data)120 Close(FreeImageIO *io, fi_handle handle, void *data) {
121 // destroy the stream wrapper
122 J2KFIO_t *fio = (J2KFIO_t*)data;
123 opj_freeimage_stream_destroy(fio);
124 }
125
126 // ----------------------------------------------------------
127
128 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)129 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
130 J2KFIO_t *fio = (J2KFIO_t*)data;
131 if (handle && fio) {
132 opj_codec_t *d_codec = NULL; // handle to a decompressor
133 opj_dparameters_t parameters; // decompression parameters
134 opj_image_t *image = NULL; // decoded image
135
136 FIBITMAP *dib = NULL;
137
138 // check the file format
139 if(!Validate(io, handle)) {
140 return NULL;
141 }
142
143 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
144
145 // get the OpenJPEG stream
146 opj_stream_t *d_stream = fio->stream;
147
148 // set decoding parameters to default values
149 opj_set_default_decoder_parameters(¶meters);
150
151 try {
152 // decode the JPEG-2000 file
153
154 // get a decoder handle
155 d_codec = opj_create_decompress(OPJ_CODEC_JP2);
156
157 // configure the event callbacks
158 // catch events using our callbacks (no local context needed here)
159 opj_set_info_handler(d_codec, NULL, NULL);
160 opj_set_warning_handler(d_codec, jp2_warning_callback, NULL);
161 opj_set_error_handler(d_codec, jp2_error_callback, NULL);
162
163 // setup the decoder decoding parameters using user parameters
164 if( !opj_setup_decoder(d_codec, ¶meters) ) {
165 throw "Failed to setup the decoder\n";
166 }
167
168 // read the main header of the codestream and if necessary the JP2 boxes
169 if( !opj_read_header(d_stream, d_codec, &image)) {
170 throw "Failed to read the header\n";
171 }
172
173 // --- header only mode
174
175 if (header_only) {
176 // create output image
177 dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
178 if(!dib) {
179 throw "Failed to import JPEG2000 image";
180 }
181 // clean-up and return header data
182 opj_destroy_codec(d_codec);
183 opj_image_destroy(image);
184 return dib;
185 }
186
187 // decode the stream and fill the image structure
188 if( !( opj_decode(d_codec, d_stream, image) && opj_end_decompress(d_codec, d_stream) ) ) {
189 throw "Failed to decode image!\n";
190 }
191
192 // free the codec context
193 opj_destroy_codec(d_codec);
194 d_codec = NULL;
195
196 // create output image
197 dib = J2KImageToFIBITMAP(s_format_id, image, header_only);
198 if(!dib) {
199 throw "Failed to import JPEG2000 image";
200 }
201
202 // free image data structure
203 opj_image_destroy(image);
204
205 return dib;
206
207 } catch (const char *text) {
208 if(dib) {
209 FreeImage_Unload(dib);
210 }
211 // free remaining structures
212 opj_destroy_codec(d_codec);
213 opj_image_destroy(image);
214
215 FreeImage_OutputMessageProc(s_format_id, text);
216
217 return NULL;
218 }
219 }
220
221 return NULL;
222 }
223
224 static BOOL DLL_CALLCONV
Save(FreeImageIO * io,FIBITMAP * dib,fi_handle handle,int page,int flags,void * data)225 Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
226 J2KFIO_t *fio = (J2KFIO_t*)data;
227 if (dib && handle && fio) {
228 BOOL bSuccess;
229 opj_codec_t *c_codec = NULL; // handle to a compressor
230 opj_cparameters_t parameters; // compression parameters
231 opj_image_t *image = NULL; // image to encode
232
233 // get the OpenJPEG stream
234 opj_stream_t *c_stream = fio->stream;
235
236 // set encoding parameters to default values
237 opj_set_default_encoder_parameters(¶meters);
238
239 try {
240 parameters.tcp_numlayers = 0;
241 // if no rate entered, apply a 16:1 rate by default
242 if(flags == JP2_DEFAULT) {
243 parameters.tcp_rates[0] = (float)16;
244 } else {
245 // for now, the flags parameter is only used to specify the rate
246 parameters.tcp_rates[0] = (float)(flags & 0x3FF);
247 }
248 parameters.tcp_numlayers++;
249 parameters.cp_disto_alloc = 1;
250
251 // convert the dib to a OpenJPEG image
252 image = FIBITMAPToJ2KImage(s_format_id, dib, ¶meters);
253 if(!image) {
254 return FALSE;
255 }
256
257 // decide if MCT should be used
258 parameters.tcp_mct = (image->numcomps == 3) ? 1 : 0;
259
260 // encode the destination image
261
262 // get a JP2 compressor handle
263 c_codec = opj_create_compress(OPJ_CODEC_JP2);
264
265 // configure the event callbacks
266 // catch events using our callbacks (no local context needed here)
267 opj_set_info_handler(c_codec, NULL, NULL);
268 opj_set_warning_handler(c_codec, jp2_warning_callback, NULL);
269 opj_set_error_handler(c_codec, jp2_error_callback, NULL);
270
271 // setup the encoder parameters using the current image and using user parameters
272 opj_setup_encoder(c_codec, ¶meters, image);
273
274 // encode the image
275 bSuccess = opj_start_compress(c_codec, image, c_stream);
276 if(bSuccess) {
277 bSuccess = bSuccess && opj_encode(c_codec, c_stream);
278 if(bSuccess) {
279 bSuccess = bSuccess && opj_end_compress(c_codec, c_stream);
280 }
281 }
282 if (!bSuccess) {
283 throw "Failed to encode image";
284 }
285
286 // free remaining compression structures
287 opj_destroy_codec(c_codec);
288
289 // free image data
290 opj_image_destroy(image);
291
292 return TRUE;
293
294 } catch (const char *text) {
295 if(c_codec) opj_destroy_codec(c_codec);
296 if(image) opj_image_destroy(image);
297 FreeImage_OutputMessageProc(s_format_id, text);
298 return FALSE;
299 }
300 }
301
302 return FALSE;
303 }
304
305 // ==========================================================
306 // Init
307 // ==========================================================
308
309 void DLL_CALLCONV
InitJP2(Plugin * plugin,int format_id)310 InitJP2(Plugin *plugin, int format_id) {
311 s_format_id = format_id;
312
313 plugin->format_proc = Format;
314 plugin->description_proc = Description;
315 plugin->extension_proc = Extension;
316 plugin->regexpr_proc = RegExpr;
317 plugin->open_proc = Open;
318 plugin->close_proc = Close;
319 plugin->pagecount_proc = NULL;
320 plugin->pagecapability_proc = NULL;
321 plugin->load_proc = Load;
322 plugin->save_proc = Save;
323 plugin->validate_proc = Validate;
324 plugin->mime_proc = MimeType;
325 plugin->supports_export_bpp_proc = SupportsExportDepth;
326 plugin->supports_export_type_proc = SupportsExportType;
327 plugin->supports_icc_profiles_proc = NULL;
328 }
329