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