1 //-----------------------------------------------------------------------------
2 //
3 // ImageLib Sources
4 // Copyright (C) 2000-2009 by Denton Woods
5 // Last modified: 03/07/2009
6 //
7 // Filename: src-IL/src/il_jp2.c
8 //
9 // Description: Jpeg-2000 (.jp2) functions
10 //
11 //-----------------------------------------------------------------------------
12 
13 
14 #include "il_internal.h"
15 #ifndef IL_NO_JP2
16 #include <jasper/jasper.h>
17 #include "il_jp2.h"
18 
19 #if defined(_WIN32) && defined(IL_USE_PRAGMA_LIBS)
20 	#if defined(_MSC_VER) || defined(__BORLANDC__)
21 		#ifndef _DEBUG
22 			#pragma comment(lib, "libjasper.lib")
23 		#else
24 			#pragma comment(lib, "libjasper-d.lib")
25 		#endif
26 	#endif
27 #endif
28 
29 ILboolean iIsValidJp2(void);
30 
31 ILboolean JasperInit = IL_FALSE;
32 
33 
34 //! Checks if the file specified in FileName is a valid .jp2 file.
ilIsValidJp2(ILconst_string FileName)35 ILboolean ilIsValidJp2(ILconst_string FileName)
36 {
37 	ILHANDLE	Jp2File;
38 	ILboolean	bJp2 = IL_FALSE;
39 
40 	if (!iCheckExtension(FileName, IL_TEXT("jp2")) && !iCheckExtension(FileName, IL_TEXT("jpx")) &&
41 		!iCheckExtension(FileName, IL_TEXT("j2k")) && !iCheckExtension(FileName, IL_TEXT("j2c"))) {
42 		ilSetError(IL_INVALID_EXTENSION);
43 		return bJp2;
44 	}
45 
46 	Jp2File = iopenr(FileName);
47 	if (Jp2File == NULL) {
48 		ilSetError(IL_COULD_NOT_OPEN_FILE);
49 		return bJp2;
50 	}
51 
52 	bJp2 = ilIsValidJp2F(Jp2File);
53 	icloser(Jp2File);
54 
55 	return bJp2;
56 }
57 
58 
59 //! Checks if the ILHANDLE contains a valid .jp2 file at the current position.
ilIsValidJp2F(ILHANDLE File)60 ILboolean ilIsValidJp2F(ILHANDLE File)
61 {
62 	ILuint		FirstPos;
63 	ILboolean	bRet;
64 
65 	iSetInputFile(File);
66 	FirstPos = itell();
67 	bRet = iIsValidJp2();
68 	iseek(FirstPos, IL_SEEK_SET);
69 
70 	return bRet;
71 }
72 
73 
74 //! Checks if Lump is a valid .jp2 lump.
ilIsValidJp2L(const void * Lump,ILuint Size)75 ILboolean ilIsValidJp2L(const void *Lump, ILuint Size)
76 {
77 	iSetInputLump(Lump, Size);
78 	return iIsValidJp2();
79 }
80 
81 
82 // Internal function to get the header and check it.
iIsValidJp2(void)83 ILboolean iIsValidJp2(void)
84 {
85 	ILubyte Signature[4];
86 
87 	iseek(4, IL_SEEK_CUR);  // Skip the 4 bytes that tell the size of the signature box.
88 	if (iread(Signature, 1, 4) != 4) {
89 		iseek(-4, IL_SEEK_CUR);
90 		return IL_FALSE;  // File read error
91 	}
92 
93 	iseek(-8, IL_SEEK_CUR);  // Restore to previous state
94 
95 	// Signature is 'jP\040\040' by the specs (or 0x6A502020).
96 	//  http://www.jpeg.org/public/fcd15444-6.pdf
97 	if (Signature[0] != 0x6A || Signature[1] != 0x50 ||
98 		Signature[2] != 0x20 || Signature[3] != 0x20)
99 		return IL_FALSE;
100 
101 	return IL_TRUE;
102 }
103 
104 
105 //! Reads a Jpeg2000 file.
ilLoadJp2(ILconst_string FileName)106 ILboolean ilLoadJp2(ILconst_string FileName)
107 {
108 	ILHANDLE	Jp2File;
109 	ILboolean	bRet;
110 
111 	Jp2File = iopenr(FileName);
112 	if (Jp2File == NULL) {
113 		ilSetError(IL_COULD_NOT_OPEN_FILE);
114 		return IL_FALSE;
115 	}
116 
117 	bRet = ilLoadJp2F(Jp2File);
118 	icloser(Jp2File);
119 
120 	return bRet;
121 }
122 
123 
124 //! Reads an already-opened Jpeg2000 file.
ilLoadJp2F(ILHANDLE File)125 ILboolean ilLoadJp2F(ILHANDLE File)
126 {
127 	ILuint			FirstPos;
128 	ILboolean		bRet;
129 	jas_stream_t	*Stream;
130 
131 	iSetInputFile(File);
132 	FirstPos = itell();
133 
134 	if (!JasperInit) {
135 		if (jas_init()) {
136 			ilSetError(IL_LIB_JP2_ERROR);
137 			return IL_FALSE;
138 		}
139 		JasperInit = IL_TRUE;
140 	}
141 	Stream = iJp2ReadStream();
142 	if (!Stream)
143 	{
144 		ilSetError(IL_COULD_NOT_OPEN_FILE);
145 		return IL_FALSE;
146 	}
147 
148 	bRet = iLoadJp2Internal(Stream, NULL);
149 	// Close the input stream.
150 	jas_stream_close(Stream);
151 
152 	iseek(FirstPos, IL_SEEK_SET);
153 
154 	return bRet;
155 }
156 
157 
158 //! Reads from a memory "lump" that contains a Jpeg2000 stream.
ilLoadJp2L(const void * Lump,ILuint Size)159 ILboolean ilLoadJp2L(const void *Lump, ILuint Size)
160 {
161 	return ilLoadJp2LInternal(Lump, Size, NULL);
162 }
163 
164 
165 //! This is separated so that it can be called for other file types, such as .icns.
ilLoadJp2LInternal(const void * Lump,ILuint Size,ILimage * Image)166 ILboolean ilLoadJp2LInternal(const void *Lump, ILuint Size, ILimage *Image)
167 {
168 	ILboolean		bRet;
169 	jas_stream_t	*Stream;
170 
171 	if (!JasperInit) {
172 		if (jas_init()) {
173 			ilSetError(IL_LIB_JP2_ERROR);
174 			return IL_FALSE;
175 		}
176 		JasperInit = IL_TRUE;
177 	}
178 	Stream = jas_stream_memopen((char*)Lump, Size);
179 	if (!Stream)
180 	{
181 		ilSetError(IL_COULD_NOT_OPEN_FILE);
182 		return IL_FALSE;
183 	}
184 
185 	bRet = iLoadJp2Internal(Stream, Image);
186 	// Close the input stream.
187 	jas_stream_close(Stream);
188 
189 	return bRet;
190 }
191 
192 
193 // Internal function used to load the Jpeg2000 stream.
iLoadJp2Internal(jas_stream_t * Stream,ILimage * Image)194 ILboolean iLoadJp2Internal(jas_stream_t	*Stream, ILimage *Image)
195 {
196 	jas_image_t		*Jp2Image = NULL;
197 	jas_matrix_t	*origdata;
198 	ILuint			x, y, c, Error;
199 	ILimage			*TempImage;
200 
201 	// Decode image
202 	Jp2Image = jas_image_decode(Stream, -1, 0);
203 	if (!Jp2Image)
204 	{
205 		ilSetError(IL_ILLEGAL_FILE_VALUE);
206 		jas_stream_close(Stream);
207 		return IL_FALSE;
208 	}
209 
210 	// JasPer likes to buffer a lot, so it may try buffering past the end
211 	//  of the file.  iread naturally sets IL_FILE_READ_ERROR if it tries
212 	//  reading past the end of the file, but this actually is not an error.
213 	Error = ilGetError();
214 	// Put the error back if it is not IL_FILE_READ_ERROR.
215 	if (Error != IL_FILE_READ_ERROR)
216 		ilSetError(Error);
217 
218 
219 	// We're not supporting anything other than 8 bits/component yet.
220 	if (jas_image_cmptprec(Jp2Image, 0) != 8)
221 	{
222 		jas_image_destroy(Jp2Image);
223 		ilSetError(IL_ILLEGAL_FILE_VALUE);
224 		return IL_FALSE;
225 	}
226 
227 	switch (jas_image_numcmpts(Jp2Image))
228 	{
229 		//@TODO: Can we do alpha data?  jas_image_cmpttype always returns 0 for this case.
230 		case 1:  // Assuming this is luminance data.
231 			if (Image == NULL) {
232 				ilTexImage(jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 1, IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL);
233 				TempImage = iCurImage;
234 			}
235 			else {
236 				ifree(Image->Data);  // @TODO: Not really the most efficient way to do this...
237 				ilInitImage(Image, jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 1, IL_LUMINANCE, IL_UNSIGNED_BYTE, NULL);
238 				TempImage = Image;
239 			}
240 			break;
241 
242 		case 2:  // Assuming this is luminance-alpha data.
243 			if (Image == NULL) {
244 				ilTexImage(jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 2, IL_LUMINANCE_ALPHA, IL_UNSIGNED_BYTE, NULL);
245 				TempImage = iCurImage;
246 			}
247 			else {
248 				ifree(Image->Data);  // @TODO: Not really the most efficient way to do this...
249 				ilInitImage(Image, jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 2, IL_LUMINANCE_ALPHA, IL_UNSIGNED_BYTE, NULL);
250 				TempImage = Image;
251 			}
252 			break;
253 
254 		case 3:
255 			if (Image == NULL) {
256 				ilTexImage(jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL);
257 				TempImage = iCurImage;
258 			}
259 			else {
260 				ifree(Image->Data);  // @TODO: Not really the most efficient way to do this...
261 				ilInitImage(Image, jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL);
262 				TempImage = Image;
263 			}
264 			break;
265 		case 4:
266 			if (Image == NULL) {
267 				ilTexImage(jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
268 				TempImage = iCurImage;
269 			}
270 			else {
271 				ifree(Image->Data);  // @TODO: Not really the most efficient way to do this...
272 				ilInitImage(Image, jas_image_width(Jp2Image), jas_image_height(Jp2Image), 1, 4, IL_RGBA, IL_UNSIGNED_BYTE, NULL);
273 				TempImage = Image;
274 			}
275 			break;
276 		default:
277 			jas_image_destroy(Jp2Image);
278 			ilSetError(IL_ILLEGAL_FILE_VALUE);
279 			return IL_FALSE;
280 	}
281 	TempImage->Origin = IL_ORIGIN_UPPER_LEFT;
282 
283 	// JasPer stores the data channels separately.
284 	//  I am assuming RGBA format.  Is it possible for other formats to be included?
285 	for (c = 0; c < TempImage->Bpp; c++)
286 	{
287 		origdata = jas_matrix_create(TempImage->Height, TempImage->Width);
288 		if (!origdata)
289 		{
290 			ilSetError(IL_LIB_JP2_ERROR);
291 			return IL_FALSE;  // @TODO: Error
292 		}
293 		// Have to convert data into an intermediate matrix format.
294 		if (jas_image_readcmpt(Jp2Image, c, 0, 0, TempImage->Width, TempImage->Height, origdata))
295 		{
296 			return IL_FALSE;
297 		}
298 
299 		for (y = 0; y < TempImage->Height; y++)
300 		{
301 			for (x = 0; x < TempImage->Width; x++)
302 			{
303 				TempImage->Data[y * TempImage->Width * TempImage->Bpp + x * TempImage->Bpp + c] = origdata->data_[y * origdata->numcols_ + x];
304 			}
305 		}
306 
307 		jas_matrix_destroy(origdata);
308 	}
309 
310 	jas_image_destroy(Jp2Image);
311 
312 	return ilFixImage();
313 }
314 
315 
316 
iJp2_file_read(jas_stream_obj_t * obj,char * buf,int cnt)317 static int iJp2_file_read(jas_stream_obj_t *obj, char *buf, int cnt)
318 {
319 	obj;
320 	return iread(buf, 1, cnt);
321 }
322 
iJp2_file_write(jas_stream_obj_t * obj,char * buf,int cnt)323 static int iJp2_file_write(jas_stream_obj_t *obj, char *buf, int cnt)
324 {
325 	obj;
326 	return iwrite(buf, 1, cnt);
327 }
328 
iJp2_file_seek(jas_stream_obj_t * obj,long offset,int origin)329 static long iJp2_file_seek(jas_stream_obj_t *obj, long offset, int origin)
330 {
331 	obj;
332 
333 	// We could just pass origin to iseek, but this is probably more portable.
334 	switch (origin)
335 	{
336 		case SEEK_SET:
337 			return iseek(offset, IL_SEEK_SET);
338 		case SEEK_CUR:
339 			return iseek(offset, IL_SEEK_CUR);
340 		case SEEK_END:
341 			return iseek(offset, IL_SEEK_END);
342 	}
343 	return 0;  // Failed
344 }
345 
iJp2_file_close(jas_stream_obj_t * obj)346 static int iJp2_file_close(jas_stream_obj_t *obj)
347 {
348 	obj;
349 	return 0;  // We choose when we want to close the file.
350 }
351 
352 static jas_stream_ops_t jas_stream_devilops = {
353 	iJp2_file_read,
354 	iJp2_file_write,
355 	iJp2_file_seek,
356 	iJp2_file_close
357 };
358 
359 static jas_stream_t *jas_stream_create(void);
360 static void jas_stream_destroy(jas_stream_t *stream);
361 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf, int bufsize);
362 
363 
364 // Modified version of jas_stream_fopen and jas_stream_memopen from jas_stream.c of JasPer
365 //  so that we can use our own file routines.
iJp2ReadStream()366 jas_stream_t *iJp2ReadStream()
367 {
368 	jas_stream_t *stream;
369 	jas_stream_memobj_t *obj;
370 
371 	if (!(stream = jas_stream_create())) {
372 		return 0;
373 	}
374 
375 	/* A stream associated with a memory buffer is always opened
376 	for both reading and writing in binary mode. */
377 	stream->openmode_ = JAS_STREAM_READ | JAS_STREAM_BINARY;
378 
379 	/* We use buffering whether it is from memory or a file. */
380 	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
381 
382 	/* Select the operations for a memory stream. */
383 	stream->ops_ = &jas_stream_devilops;
384 
385 	/* Allocate memory for the underlying memory stream object. */
386 	if (!(obj = jas_malloc(sizeof(jas_stream_memobj_t)))) {
387 		jas_stream_destroy(stream);
388 		return 0;
389 	}
390 	stream->obj_ = (void *) obj;
391 
392 	/* Initialize a few important members of the memory stream object. */
393 	obj->myalloc_ = 0;
394 	obj->buf_ = 0;
395 
396 	// Shouldn't need any of this.
397 
398 	///* If the buffer size specified is nonpositive, then the buffer
399 	//is allocated internally and automatically grown as needed. */
400 	//if (bufsize <= 0) {
401 	//	obj->bufsize_ = 1024;
402 	//	obj->growable_ = 1;
403 	//} else {
404 	//	obj->bufsize_ = bufsize;
405 	//	obj->growable_ = 0;
406 	//}
407 	//if (buf) {
408 	//	obj->buf_ = (unsigned char *) buf;
409 	//} else {
410 	//	obj->buf_ = jas_malloc(obj->bufsize_ * sizeof(char));
411 	//	obj->myalloc_ = 1;
412 	//}
413 	//if (!obj->buf_) {
414 	//	jas_stream_close(stream);
415 	//	return 0;
416 	//}
417 
418 	//if (bufsize > 0 && buf) {
419 	//	/* If a buffer was supplied by the caller and its length is positive,
420 	//	  make the associated buffer data appear in the stream initially. */
421 	//	obj->len_ = bufsize;
422 	//} else {
423 	//	/* The stream is initially empty. */
424 	//	obj->len_ = 0;
425 	//}
426 	//obj->pos_ = 0;
427 
428 	return stream;
429 }
430 
431 
432 // The following functions are taken directly from jas_stream.c of JasPer,
433 //  since they are designed to be used within JasPer only.
434 
jas_stream_initbuf(jas_stream_t * stream,int bufmode,char * buf,int bufsize)435 static void jas_stream_initbuf(jas_stream_t *stream, int bufmode, char *buf,
436   int bufsize)
437 {
438 	/* If this function is being called, the buffer should not have been
439 	  initialized yet. */
440 	assert(!stream->bufbase_);
441 
442 	if (bufmode != JAS_STREAM_UNBUF) {
443 		/* The full- or line-buffered mode is being employed. */
444 		if (!buf) {
445 			/* The caller has not specified a buffer to employ, so allocate
446 			  one. */
447 			if ((stream->bufbase_ = jas_malloc(JAS_STREAM_BUFSIZE +
448 			  JAS_STREAM_MAXPUTBACK))) {
449 				stream->bufmode_ |= JAS_STREAM_FREEBUF;
450 				stream->bufsize_ = JAS_STREAM_BUFSIZE;
451 			} else {
452 				/* The buffer allocation has failed.  Resort to unbuffered
453 				  operation. */
454 				stream->bufbase_ = stream->tinybuf_;
455 				stream->bufsize_ = 1;
456 			}
457 		} else {
458 			/* The caller has specified a buffer to employ. */
459 			/* The buffer must be large enough to accommodate maximum
460 			  putback. */
461 			assert(bufsize > JAS_STREAM_MAXPUTBACK);
462 			stream->bufbase_ = JAS_CAST(jas_uchar *, buf);
463 			stream->bufsize_ = bufsize - JAS_STREAM_MAXPUTBACK;
464 		}
465 	} else {
466 		/* The unbuffered mode is being employed. */
467 		/* A buffer should not have been supplied by the caller. */
468 		assert(!buf);
469 		/* Use a trivial one-character buffer. */
470 		stream->bufbase_ = stream->tinybuf_;
471 		stream->bufsize_ = 1;
472 	}
473 	stream->bufstart_ = &stream->bufbase_[JAS_STREAM_MAXPUTBACK];
474 	stream->ptr_ = stream->bufstart_;
475 	stream->cnt_ = 0;
476 	stream->bufmode_ |= bufmode & JAS_STREAM_BUFMODEMASK;
477 }
478 
jas_stream_create()479 static jas_stream_t *jas_stream_create()
480 {
481 	jas_stream_t *stream;
482 
483 	if (!(stream = jas_malloc(sizeof(jas_stream_t)))) {
484 		return 0;
485 	}
486 	stream->openmode_ = 0;
487 	stream->bufmode_ = 0;
488 	stream->flags_ = 0;
489 	stream->bufbase_ = 0;
490 	stream->bufstart_ = 0;
491 	stream->bufsize_ = 0;
492 	stream->ptr_ = 0;
493 	stream->cnt_ = 0;
494 	stream->ops_ = 0;
495 	stream->obj_ = 0;
496 	stream->rwcnt_ = 0;
497 	stream->rwlimit_ = -1;
498 
499 	return stream;
500 }
501 
jas_stream_destroy(jas_stream_t * stream)502 static void jas_stream_destroy(jas_stream_t *stream)
503 {
504 	/* If the memory for the buffer was allocated with malloc, free
505 	this memory. */
506 	if ((stream->bufmode_ & JAS_STREAM_FREEBUF) && stream->bufbase_) {
507 		jas_free(stream->bufbase_);
508 		stream->bufbase_ = 0;
509 	}
510 	jas_free(stream);
511 }
512 
513 
514 
515 
iJp2WriteStream()516 jas_stream_t *iJp2WriteStream()
517 {
518 	jas_stream_t *stream;
519 	jas_stream_memobj_t *obj;
520 
521 	if (!(stream = jas_stream_create())) {
522 		return 0;
523 	}
524 
525 	/* A stream associated with a memory buffer is always opened
526 	for both reading and writing in binary mode. */
527 	stream->openmode_ = JAS_STREAM_WRITE | JAS_STREAM_BINARY;
528 
529 	/* We use buffering whether it is from memory or a file. */
530 	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
531 
532 	/* Select the operations for a memory stream. */
533 	stream->ops_ = &jas_stream_devilops;
534 
535 	/* Allocate memory for the underlying memory stream object. */
536 	if (!(obj = jas_malloc(sizeof(jas_stream_memobj_t)))) {
537 		jas_stream_destroy(stream);
538 		return 0;
539 	}
540 	stream->obj_ = (void *) obj;
541 
542 	/* Initialize a few important members of the memory stream object. */
543 	obj->myalloc_ = 0;
544 	obj->buf_ = 0;
545 
546 	return stream;
547 }
548 
549 
550 
551 //! Writes a Jp2 file
ilSaveJp2(const ILstring FileName)552 ILboolean ilSaveJp2(const ILstring FileName)
553 {
554 	ILHANDLE	Jp2File;
555 	ILuint		Jp2Size;
556 
557 	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
558 		if (iFileExists(FileName)) {
559 			ilSetError(IL_FILE_ALREADY_EXISTS);
560 			return IL_FALSE;
561 		}
562 	}
563 
564 	Jp2File = iopenw(FileName);
565 	if (Jp2File == NULL) {
566 		ilSetError(IL_COULD_NOT_OPEN_FILE);
567 		return IL_FALSE;
568 	}
569 
570 	Jp2Size = ilSaveJp2F(Jp2File);
571 	iclosew(Jp2File);
572 
573 	if (Jp2Size == 0)
574 		return IL_FALSE;
575 	return IL_TRUE;
576 }
577 
578 
579 //! Writes a Jp2 to an already-opened file
ilSaveJp2F(ILHANDLE File)580 ILuint ilSaveJp2F(ILHANDLE File)
581 {
582 	ILuint Pos;
583 	iSetOutputFile(File);
584 	Pos = itellw();
585 	if (iSaveJp2Internal() == IL_FALSE)
586 		return 0;  // Error occurred
587 	return itellw() - Pos;  // Return the number of bytes written.
588 }
589 
590 
591 //! Writes a Jp2 to a memory "lump"
ilSaveJp2L(void * Lump,ILuint Size)592 ILuint ilSaveJp2L(void *Lump, ILuint Size)
593 {
594 	ILuint Pos;
595 	iSetOutputLump(Lump, Size);
596 	Pos = itellw();
597 	if (iSaveJp2Internal() == IL_FALSE)
598 		return 0;  // Error occurred
599 	return itellw() - Pos;  // Return the number of bytes written.
600 }
601 
602 
603 
604 // Function from OpenSceneGraph (originally called getdata in their sources):
605 //  http://openscenegraph.sourcearchive.com/documentation/2.2.0/ReaderWriterJP2_8cpp-source.html
Jp2ConvertData(jas_stream_t * in,jas_image_t * image)606 ILint Jp2ConvertData(jas_stream_t *in, jas_image_t *image)
607 {
608 	int ret;
609 	int numcmpts;
610 	int cmptno;
611 	jas_matrix_t *data[4];
612 	int x;
613 	int y;
614 	int width, height;
615 
616 	width = jas_image_cmptwidth(image, 0);
617 	height = jas_image_cmptheight(image, 0);
618 	numcmpts = jas_image_numcmpts(image);
619 
620 	ret = -1;
621 
622 	data[0] = 0;
623 	data[1] = 0;
624 	data[2] = 0;
625 	data[3] = 0;
626 	for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
627 		if (!(data[cmptno] = jas_matrix_create(1, width))) {
628 			goto done;
629 		}
630 	}
631 
632 	for (y = height - 1; y >= 0; --y)
633 	//        for (y = 0; y < height; ++y)
634 	{
635 		for (x = 0; x < width; ++x)
636 		{
637 			for (cmptno = 0; cmptno < numcmpts; ++cmptno)
638 			{
639 				// The sample data is unsigned.
640 				int c;
641 				if ((c = jas_stream_getc(in)) == EOF) {
642 					return -1;
643 				}
644 				jas_matrix_set(data[cmptno], 0, x, c);
645 			}
646 		}
647 		for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
648 			if (jas_image_writecmpt(image, cmptno, 0, y, width, 1,
649 				data[cmptno])) {
650 				goto done;
651 			}
652 		}
653 	}
654 
655 	jas_stream_flush(in);
656 	ret = 0;
657 
658 done:
659 	for (cmptno = 0; cmptno < numcmpts; ++cmptno) {
660 		if (data[cmptno]) {
661 			jas_matrix_destroy(data[cmptno]);
662 		}
663 	}
664 
665 	return ret;
666 }
667 
668 
669 // Internal function used to save the Jp2.
670 // Since the JasPer documentation is extremely incomplete, I had to look at how OpenSceneGraph uses it:
671 //  http://openscenegraph.sourcearchive.com/documentation/2.2.0/ReaderWriterJP2_8cpp-source.html
672 
673 //@TODO: Do we need to worry about images with depths > 1?
iSaveJp2Internal()674 ILboolean iSaveJp2Internal()
675 {
676 	jas_image_t *Jp2Image;
677 	jas_image_cmptparm_t cmptparm[4];
678 	jas_stream_t *Mem, *Stream;
679 	ILuint	NumChans, i;
680 	ILenum	NewFormat, NewType = IL_UNSIGNED_BYTE;
681 	ILimage	*TempImage = iCurImage;
682 
683 	if (!JasperInit) {
684 		if (jas_init()) {
685 			ilSetError(IL_LIB_JP2_ERROR);
686 			return IL_FALSE;
687 		}
688 		JasperInit = IL_TRUE;
689 	}
690 
691 	if (iCurImage->Type != IL_UNSIGNED_BYTE) {  //@TODO: Support greater than 1 bpc.
692 		NewType = IL_UNSIGNED_BYTE;
693 	}
694 	//@TODO: Do luminance/luminance-alpha/alpha separately.
695 	switch (iCurImage->Format)
696 	{
697 		case IL_LUMINANCE:
698 			NewFormat = IL_LUMINANCE;
699 			NumChans = 1;
700 			break;
701 		case IL_ALPHA:
702 			NewFormat = IL_ALPHA;
703 			NumChans = 1;
704 			break;
705 		case IL_LUMINANCE_ALPHA:
706 			NewFormat = IL_LUMINANCE_ALPHA;
707 			NumChans = 2;
708 			break;
709 		case IL_COLOUR_INDEX:  // Assuming the color palette does not have an alpha value.
710 								//@TODO: Check for this in the future.
711 		case IL_RGB:
712 		case IL_BGR:
713 			NewFormat = IL_RGB;
714 			NumChans = 3;
715 			break;
716 		case IL_RGBA:
717 		case IL_BGRA:
718 			NewFormat = IL_RGBA;
719 			NumChans = 4;
720 			break;
721 	}
722 
723 	if (NewType != iCurImage->Type || NewFormat != iCurImage->Format) {
724 		TempImage = iConvertImage(iCurImage, NewFormat, NewType);
725 		if (TempImage == NULL)
726 			return IL_FALSE;
727 	}
728 
729 	// The origin is always in the lower left corner.  Flip the buffer if it is not.
730 	if (TempImage->Origin == IL_ORIGIN_UPPER_LEFT)
731 		iFlipBuffer(TempImage->Data, TempImage->Depth, TempImage->Bps, TempImage->Height);
732 
733 	// Have to tell JasPer about each channel.  In our case, they all have the same information.
734 	for (i = 0; i < NumChans; i++) {
735 		cmptparm[i].width = iCurImage->Width;
736 		cmptparm[i].height = iCurImage->Height;
737 		cmptparm[i].hstep = 1;
738 		cmptparm[i].vstep = 1;
739 		cmptparm[i].tlx = 0;
740 		cmptparm[i].tly = 0;
741 		cmptparm[i].prec = 8;
742 		cmptparm[i].sgnd = 0;  // Unsigned data
743 	}
744 
745 	// Using the unknown color space, since we have not determined the space yet.
746 	//  This is done in the following switch statement.
747 	Jp2Image = jas_image_create(NumChans, cmptparm, JAS_CLRSPC_UNKNOWN);
748 	if (Jp2Image == NULL) {
749 		ilSetError(IL_LIB_JP2_ERROR);
750 		return IL_FALSE;
751 	}
752 
753 	switch (NumChans)
754 	{
755 		case 1:
756 			jas_image_setclrspc(Jp2Image, JAS_CLRSPC_SGRAY);
757 			if (NewFormat == IL_LUMINANCE)
758 				jas_image_setcmpttype(Jp2Image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
759 			else // IL_ALPHA
760 				jas_image_setcmpttype(Jp2Image, 0, JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
761 			break;
762 		case 2:
763 			jas_image_setclrspc(Jp2Image, JAS_CLRSPC_SGRAY);
764 			jas_image_setcmpttype(Jp2Image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
765 			jas_image_setcmpttype(Jp2Image, 1, JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
766 			break;
767 		case 3:
768 			jas_image_setclrspc(Jp2Image, JAS_CLRSPC_SRGB);
769 			jas_image_setcmpttype(Jp2Image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
770 			jas_image_setcmpttype(Jp2Image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
771 			jas_image_setcmpttype(Jp2Image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
772 			break;
773 		case 4:
774 			jas_image_setclrspc(Jp2Image, JAS_CLRSPC_SRGB);
775 			jas_image_setcmpttype(Jp2Image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
776 			jas_image_setcmpttype(Jp2Image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
777 			jas_image_setcmpttype(Jp2Image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
778 			jas_image_setcmpttype(Jp2Image, 3, JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY));
779 			break;
780 	}
781 
782 	Mem = jas_stream_memopen((char*)TempImage->Data, TempImage->SizeOfData);
783 	if (Mem == NULL) {
784 		jas_image_destroy(Jp2Image);
785 		ilSetError(IL_LIB_JP2_ERROR);
786 		return IL_FALSE;
787 	}
788 	Stream = iJp2WriteStream();
789 	if (Stream == NULL) {
790 		jas_stream_close(Mem);
791 		jas_image_destroy(Jp2Image);
792 		ilSetError(IL_LIB_JP2_ERROR);
793 		return IL_FALSE;
794 	}
795 
796 	// Puts data in the format that JasPer wants it in.
797 	Jp2ConvertData(Mem, Jp2Image);
798 
799 	// Does all of the encoding.
800 	if (jas_image_encode(Jp2Image, Stream, jas_image_strtofmt("jp2"), NULL)) {  //@TODO: Do we want to use any options?
801 		jas_stream_close(Mem);
802 		jas_stream_close(Stream);
803 		jas_image_destroy(Jp2Image);
804 		ilSetError(IL_LIB_JP2_ERROR);
805 		return IL_FALSE;
806 	}
807 	jas_stream_flush(Stream);  // Do any final writing.
808 
809 	// Close the memory and output streams.
810 	jas_stream_close(Mem);
811 	jas_stream_close(Stream);
812 	// Destroy the JasPer image.
813 	jas_image_destroy(Jp2Image);
814 
815 	// Destroy our temporary image if we used one.
816 	if (TempImage != iCurImage)
817 		ilCloseImage(TempImage);
818 
819 	return IL_TRUE;
820 }
821 
822 
823 #endif//IL_NO_JP2
824