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(&parameters);
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, &parameters) ) {
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(&parameters);
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, &parameters);
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, &parameters, 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