blob: 6c00cbb024f003fe900ff57d860ce96f68e9f402 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% JJJ PPPP 222 %
7% J P P 2 2 %
8% J PPPP 22 %
9% J J P 2 %
10% JJ P 22222 %
11% %
12% %
13% Read/Write JPEG-2000 Image Format %
14% %
15% John Cristy %
16% Nathan Brown %
17% June 2001 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
42#include "magick/studio.h"
cristy5a2ca482009-10-14 18:24:56 +000043#include "magick/attribute.h"
cristy3ed852e2009-09-05 21:47:34 +000044#include "magick/blob.h"
45#include "magick/blob-private.h"
46#include "magick/cache.h"
47#include "magick/colorspace.h"
48#include "magick/color.h"
49#include "magick/color-private.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/image.h"
53#include "magick/image-private.h"
54#include "magick/list.h"
55#include "magick/magick.h"
56#include "magick/memory_.h"
57#include "magick/monitor.h"
58#include "magick/monitor-private.h"
59#include "magick/option.h"
60#include "magick/profile.h"
61#include "magick/quantum-private.h"
62#include "magick/static.h"
63#include "magick/statistic.h"
64#include "magick/string_.h"
65#include "magick/module.h"
66#if defined(MAGICKCORE_JP2_DELEGATE)
67#ifndef JAS_IMAGE_CM_GRAY
68#define JAS_IMAGE_CM_GRAY JAS_IMAGE_CS_GRAY
69#endif
70#ifndef JAS_IMAGE_CM_RGB
71#define JAS_IMAGE_CM_RGB JAS_IMAGE_CS_RGB
72#endif
73#if !defined(uchar)
74#define uchar unsigned char
75#endif
76#if !defined(ushort)
77#define ushort unsigned short
78#endif
79#if !defined(uint)
80#define uint unsigned int
81#endif
cristybb503372010-05-27 20:51:26 +000082#if !defined(ssize_tssize_t)
83#define ssize_tssize_t long long
cristy3ed852e2009-09-05 21:47:34 +000084#endif
cristybb503372010-05-27 20:51:26 +000085#if !defined(ussize_tssize_t)
86#define ussize_tssize_t unsigned long long
cristy3ed852e2009-09-05 21:47:34 +000087#endif
88
cristy30faca22009-09-29 13:49:52 +000089#undef PACKAGE_NAME
90#undef PACKAGE_STRING
91#undef PACKAGE_TARNAME
92#undef PACKAGE_VERSION
cristy3ed852e2009-09-05 21:47:34 +000093#include "jasper/jasper.h"
cristy30faca22009-09-29 13:49:52 +000094#undef PACKAGE_NAME
95#undef PACKAGE_STRING
96#undef PACKAGE_TARNAME
97#undef PACKAGE_VERSION
98
cristy3ed852e2009-09-05 21:47:34 +000099#endif
100
101/*
102 Forward declarations.
103*/
104#if defined(MAGICKCORE_JP2_DELEGATE)
105static MagickBooleanType
106 WriteJP2Image(const ImageInfo *,Image *);
cristy1e09ca22009-12-27 18:04:58 +0000107
108static volatile MagickBooleanType
109 instantiate_jp2 = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000110#endif
111
112/*
113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114% %
115% %
116% %
117% I s J P 2 %
118% %
119% %
120% %
121%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
122%
123% IsJP2() returns MagickTrue if the image format type, identified by the
124% magick string, is JP2.
125%
126% The format of the IsJP2 method is:
127%
128% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
129%
130% A description of each parameter follows:
131%
132% o magick: compare image format pattern against these bytes.
133%
134% o length: Specifies the length of the magick string.
135%
136*/
137static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
138{
139 if (length < 9)
140 return(MagickFalse);
141 if (memcmp(magick+4,"\152\120\040\040\015",5) == 0)
142 return(MagickTrue);
143 return(MagickFalse);
144}
145
146/*
147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148% %
149% %
150% %
151% I s J P C %
152% %
153% %
154% %
155%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156%
157% IsJPC()() returns MagickTrue if the image format type, identified by the
158% magick string, is JPC.
159%
160% The format of the IsJPC method is:
161%
162% MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
163%
164% A description of each parameter follows:
165%
166% o magick: compare image format pattern against these bytes.
167%
168% o length: Specifies the length of the magick string.
169%
170*/
171static MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
172{
173 if (length < 2)
174 return(MagickFalse);
175 if (memcmp(magick,"\377\117",2) == 0)
176 return(MagickTrue);
177 return(MagickFalse);
178}
179
180/*
181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182% %
183% %
184% %
185% R e a d J P 2 I m a g e %
186% %
187% %
188% %
189%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190%
191% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
192% codestream (JPC) image file and returns it. It allocates the memory
193% necessary for the new Image structure and returns a pointer to the new
194% image or set of images.
195%
196% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
197%
198% The format of the ReadJP2Image method is:
199%
200% Image *ReadJP2Image(const ImageInfo *image_info,
201% ExceptionInfo *exception)
202%
203% A description of each parameter follows:
204%
205% o image_info: the image info.
206%
207% o exception: return any errors or warnings in this structure.
208%
209*/
210#if defined(MAGICKCORE_JP2_DELEGATE)
211
212typedef struct _StreamManager
213{
214 jas_stream_t
215 *stream;
216
217 Image
218 *image;
219} StreamManager;
220
221static int BlobRead(jas_stream_obj_t *object,char *buffer,const int length)
222{
223 ssize_t
224 count;
225
226 StreamManager
227 *source;
228
229 source=(StreamManager *) object;
230 count=ReadBlob(source->image,(size_t) length,(unsigned char *) buffer);
231 return((int) count);
232}
233
234static int BlobWrite(jas_stream_obj_t *object,char *buffer,const int length)
235{
236 ssize_t
237 count;
238
239 StreamManager
240 *source;
241
242 source=(StreamManager *) object;
243 count=WriteBlob(source->image,(size_t) length,(unsigned char *) buffer);
244 return((int) count);
245}
246
cristyf1d91242010-05-28 02:23:19 +0000247static long BlobSeek(jas_stream_obj_t *object,long offset,int origin)
cristy3ed852e2009-09-05 21:47:34 +0000248{
249 StreamManager
250 *source;
251
252 source=(StreamManager *) object;
cristyf1d91242010-05-28 02:23:19 +0000253 return((long) SeekBlob(source->image,offset,origin));
cristy3ed852e2009-09-05 21:47:34 +0000254}
255
256static int BlobClose(jas_stream_obj_t *object)
257{
258 StreamManager
259 *source;
260
261 source=(StreamManager *) object;
262 (void) CloseBlob(source->image);
cristyb41ee102010-10-04 16:46:15 +0000263 free(source);
264 source=(StreamManager *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000265 return(0);
266}
267
268static inline size_t MagickMax(const size_t x,const size_t y)
269{
270 if (x > y)
271 return(x);
272 return(y);
273}
274
275static inline size_t MagickMin(const size_t x,const size_t y)
276{
277 if (x < y)
278 return(x);
279 return(y);
280}
281
282static jas_stream_t *JP2StreamManager(Image *image)
283{
284 static jas_stream_ops_t
285 StreamOperators =
286 {
287 BlobRead,
288 BlobWrite,
289 BlobSeek,
290 BlobClose
291 };
292
293 jas_stream_t
294 *stream;
295
296 StreamManager
297 *source;
298
299 stream=(jas_stream_t *) jas_malloc(sizeof(*stream));
300 if (stream == (jas_stream_t *) NULL)
301 return((jas_stream_t *) NULL);
302 (void) ResetMagickMemory(stream,0,sizeof(*stream));
303 stream->rwlimit_=(-1);
304 stream->obj_=(jas_stream_obj_t *) jas_malloc(sizeof(StreamManager));
305 if (stream->obj_ == (jas_stream_obj_t *) NULL)
306 return((jas_stream_t *) NULL);
307 (void) ResetMagickMemory(stream->obj_,0,sizeof(StreamManager));
308 stream->ops_=(&StreamOperators);
309 stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
310 stream->bufbase_=(unsigned char *) jas_malloc(JAS_STREAM_BUFSIZE+
311 JAS_STREAM_MAXPUTBACK);
312 if (stream->bufbase_ == (void *) NULL)
313 {
314 stream->bufbase_=stream->tinybuf_;
315 stream->bufsize_=1;
316 }
317 else
318 {
319 stream->bufmode_=JAS_STREAM_FREEBUF | JAS_STREAM_BUFMODEMASK;
320 stream->bufsize_=JAS_STREAM_BUFSIZE;
321 }
322 stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
323 stream->ptr_=stream->bufstart_;
324 stream->cnt_=0;
325 source=(StreamManager *) stream->obj_;
326 source->image=image;
327 return(stream);
328}
329
330static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
331{
332 Image
333 *image;
334
cristy3ed852e2009-09-05 21:47:34 +0000335 jas_cmprof_t
336 *cm_profile;
337
338 jas_iccprof_t
339 *icc_profile;
340
341 jas_image_t
342 *jp2_image;
343
344 jas_matrix_t
345 *pixels[4];
346
347 jas_stream_t
348 *jp2_stream;
349
cristybb503372010-05-27 20:51:26 +0000350 ssize_t
cristy18f8ee42009-09-30 01:04:36 +0000351 components[4],
352 y;
353
cristy3ed852e2009-09-05 21:47:34 +0000354 MagickBooleanType
355 status;
356
357 QuantumAny
cristy30faca22009-09-29 13:49:52 +0000358 pixel,
cristy722af032009-12-04 01:50:06 +0000359 range[4];
cristy3ed852e2009-09-05 21:47:34 +0000360
cristybb503372010-05-27 20:51:26 +0000361 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000362 i,
363 x;
364
365 register PixelPacket
366 *q;
367
cristybb503372010-05-27 20:51:26 +0000368 size_t
cristy3ed852e2009-09-05 21:47:34 +0000369 maximum_component_depth,
370 number_components,
cristy3ed852e2009-09-05 21:47:34 +0000371 x_step[4],
372 y_step[4];
373
374 /*
375 Open image file.
376 */
377 assert(image_info != (const ImageInfo *) NULL);
378 assert(image_info->signature == MagickSignature);
379 if (image_info->debug != MagickFalse)
380 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
381 image_info->filename);
382 assert(exception != (ExceptionInfo *) NULL);
383 assert(exception->signature == MagickSignature);
384 image=AcquireImage(image_info);
385 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
386 if (status == MagickFalse)
387 {
388 image=DestroyImageList(image);
389 return((Image *) NULL);
390 }
391 /*
392 Initialize JPEG 2000 API.
393 */
394 jp2_stream=JP2StreamManager(image);
395 if (jp2_stream == (jas_stream_t *) NULL)
396 ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
397 jp2_image=jas_image_decode(jp2_stream,-1,0);
398 if (jp2_image == (jas_image_t *) NULL)
399 {
400 (void) jas_stream_close(jp2_stream);
401 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
402 }
cristy3ed852e2009-09-05 21:47:34 +0000403 switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
404 {
405 case JAS_CLRSPC_FAM_RGB:
406 {
407 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_R);
408 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_G);
409 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_B);
410 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
411 {
412 (void) jas_stream_close(jp2_stream);
413 jas_image_destroy(jp2_image);
414 ThrowReaderException(CorruptImageError,"MissingImageChannel");
415 }
416 number_components=3;
417 components[3]=jas_image_getcmptbytype(jp2_image,3);
418 if (components[3] > 0)
419 {
420 image->matte=MagickTrue;
421 number_components++;
422 }
423 break;
424 }
425 case JAS_CLRSPC_FAM_GRAY:
426 {
427 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_GRAY_Y);
428 if (components[0] < 0)
429 {
430 (void) jas_stream_close(jp2_stream);
431 jas_image_destroy(jp2_image);
432 ThrowReaderException(CorruptImageError,"MissingImageChannel");
433 }
434 number_components=1;
435 break;
436 }
437 case JAS_CLRSPC_FAM_YCBCR:
438 {
439 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_Y);
440 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CB);
441 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CR);
442 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
443 {
444 (void) jas_stream_close(jp2_stream);
445 jas_image_destroy(jp2_image);
446 ThrowReaderException(CorruptImageError,"MissingImageChannel");
447 }
448 number_components=3;
449 components[3]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_UNKNOWN);
450 if (components[3] > 0)
451 {
452 image->matte=MagickTrue;
453 number_components++;
454 }
455 image->colorspace=YCbCrColorspace;
456 break;
457 }
458 default:
459 {
460 (void) jas_stream_close(jp2_stream);
461 jas_image_destroy(jp2_image);
462 ThrowReaderException(CoderError,"ColorspaceModelIsNotSupported");
463 }
464 }
cristy30faca22009-09-29 13:49:52 +0000465 image->columns=jas_image_width(jp2_image);
466 image->rows=jas_image_height(jp2_image);
cristy3ed852e2009-09-05 21:47:34 +0000467 image->compression=JPEG2000Compression;
cristybb503372010-05-27 20:51:26 +0000468 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000469 {
cristybb503372010-05-27 20:51:26 +0000470 size_t
cristy3ed852e2009-09-05 21:47:34 +0000471 height,
472 width;
473
cristybb503372010-05-27 20:51:26 +0000474 width=(size_t) (jas_image_cmptwidth(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000475 jas_image_cmpthstep(jp2_image,components[i]));
cristybb503372010-05-27 20:51:26 +0000476 height=(size_t) (jas_image_cmptheight(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000477 jas_image_cmptvstep(jp2_image,components[i]));
cristy30faca22009-09-29 13:49:52 +0000478 x_step[i]=(unsigned int) jas_image_cmpthstep(jp2_image,components[i]);
479 y_step[i]=(unsigned int) jas_image_cmptvstep(jp2_image,components[i]);
cristy3ed852e2009-09-05 21:47:34 +0000480 if ((width != image->columns) || (height != image->rows) ||
481 (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
cristy30faca22009-09-29 13:49:52 +0000482 (jas_image_cmpttly(jp2_image,components[i]) != 0) ||
483 (x_step[i] != 1) || (y_step[i] != 1) ||
484 (jas_image_cmptsgnd(jp2_image,components[i]) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000485 {
486 (void) jas_stream_close(jp2_stream);
487 jas_image_destroy(jp2_image);
488 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported");
489 }
cristy3ed852e2009-09-05 21:47:34 +0000490 }
491 /*
492 Convert JPEG 2000 pixels.
493 */
494 image->matte=number_components > 3 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000495 maximum_component_depth=0;
cristybb503372010-05-27 20:51:26 +0000496 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000497 {
498 maximum_component_depth=(unsigned int) MagickMax((size_t)
499 jas_image_cmptprec(jp2_image,components[i]),(size_t)
500 maximum_component_depth);
501 pixels[i]=jas_matrix_create(1,(int) (image->columns/x_step[i]));
502 if (pixels[i] == (jas_matrix_t *) NULL)
503 {
cristy30faca22009-09-29 13:49:52 +0000504 for (--i; i >= 0; i--)
505 jas_matrix_destroy(pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000506 jas_image_destroy(jp2_image);
507 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
508 }
509 }
cristy30faca22009-09-29 13:49:52 +0000510 image->depth=maximum_component_depth;
511 if (image_info->ping != MagickFalse)
512 {
513 (void) jas_stream_close(jp2_stream);
514 jas_image_destroy(jp2_image);
515 return(GetFirstImageInList(image));
516 }
cristybb503372010-05-27 20:51:26 +0000517 for (i=0; i < (ssize_t) number_components; i++)
518 range[i]=GetQuantumRange((size_t) jas_image_cmptprec(jp2_image,
cristy3ed852e2009-09-05 21:47:34 +0000519 components[i]));
cristybb503372010-05-27 20:51:26 +0000520 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000521 {
522 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
523 if (q == (PixelPacket *) NULL)
524 break;
cristybb503372010-05-27 20:51:26 +0000525 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000526 (void) jas_image_readcmpt(jp2_image,(short) components[i],0,
cristyf6fe0a12010-05-30 00:44:47 +0000527 (jas_image_coord_t) (y/y_step[i]),(jas_image_coord_t) (image->columns/
528 x_step[i]),1,pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000529 switch (number_components)
530 {
531 case 1:
532 {
533 /*
534 Grayscale.
535 */
cristybb503372010-05-27 20:51:26 +0000536 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000537 {
538 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy722af032009-12-04 01:50:06 +0000539 q->red=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[0]);
cristy3ed852e2009-09-05 21:47:34 +0000540 q->green=q->red;
541 q->blue=q->red;
542 q++;
543 }
544 break;
545 }
546 case 3:
547 {
548 /*
549 RGB.
550 */
cristybb503372010-05-27 20:51:26 +0000551 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000552 {
553 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy722af032009-12-04 01:50:06 +0000554 q->red=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[0]);
cristy3ed852e2009-09-05 21:47:34 +0000555 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy722af032009-12-04 01:50:06 +0000556 q->green=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[1]);
cristy3ed852e2009-09-05 21:47:34 +0000557 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy722af032009-12-04 01:50:06 +0000558 q->blue=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[2]);
cristy3ed852e2009-09-05 21:47:34 +0000559 q++;
560 }
561 break;
562 }
563 case 4:
564 {
565 /*
566 RGBA.
567 */
cristybb503372010-05-27 20:51:26 +0000568 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000569 {
570 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy722af032009-12-04 01:50:06 +0000571 q->red=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[0]);
cristy3ed852e2009-09-05 21:47:34 +0000572 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy722af032009-12-04 01:50:06 +0000573 q->green=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[1]);
cristy3ed852e2009-09-05 21:47:34 +0000574 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy722af032009-12-04 01:50:06 +0000575 q->blue=(Quantum) ScaleAnyToQuantum((QuantumAny) pixel,range[2]);
cristy3ed852e2009-09-05 21:47:34 +0000576 pixel=(QuantumAny) jas_matrix_getv(pixels[3],x/x_step[3]);
cristy722af032009-12-04 01:50:06 +0000577 q->opacity=(Quantum) (QuantumRange-
578 ScaleAnyToQuantum((QuantumAny) pixel,range[3]));
cristy3ed852e2009-09-05 21:47:34 +0000579 q++;
580 }
581 break;
582 }
583 }
584 if (SyncAuthenticPixels(image,exception) == MagickFalse)
585 break;
cristycee97112010-05-28 00:44:52 +0000586 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
587 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000588 if (status == MagickFalse)
589 break;
590 }
591 cm_profile=jas_image_cmprof(jp2_image);
592 icc_profile=(jas_iccprof_t *) NULL;
593 if (cm_profile != (jas_cmprof_t *) NULL)
594 icc_profile=jas_iccprof_createfromcmprof(cm_profile);
595 if (icc_profile != (jas_iccprof_t *) NULL)
596 {
597 jas_stream_t
598 *icc_stream;
599
600 icc_stream=jas_stream_memopen(NULL,0);
601 if ((icc_stream != (jas_stream_t *) NULL) &&
602 (jas_iccprof_save(icc_profile,icc_stream) == 0) &&
603 (jas_stream_flush(icc_stream) == 0))
604 {
605 StringInfo
606 *icc_profile,
607 *profile;
608
609 jas_stream_memobj_t
610 *blob;
611
612 /*
613 Extract the icc profile, handle errors without much noise.
614 */
615 blob=(jas_stream_memobj_t *) icc_stream->obj_;
616 if (image->debug != MagickFalse)
617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000618 "Profile: ICC, %.20g bytes",(double) blob->len_);
cristy3ed852e2009-09-05 21:47:34 +0000619 profile=AcquireStringInfo(blob->len_);
620 SetStringInfoDatum(profile,blob->buf_);
621 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
622 if (icc_profile == (StringInfo *) NULL)
623 (void) SetImageProfile(image,"icc",profile);
624 else
625 (void) ConcatenateStringInfo(icc_profile,profile);
626 profile=DestroyStringInfo(profile);
627 (void) jas_stream_close(icc_stream);
628 }
629 }
630 (void) jas_stream_close(jp2_stream);
631 jas_image_destroy(jp2_image);
cristybb503372010-05-27 20:51:26 +0000632 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000633 jas_matrix_destroy(pixels[i]);
634 return(GetFirstImageInList(image));
635}
636#endif
637
638/*
639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640% %
641% %
642% %
643% R e g i s t e r J P 2 I m a g e %
644% %
645% %
646% %
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648%
649% RegisterJP2Image() adds attributes for the JP2 image format to the list of
650% supported formats. The attributes include the image format tag, a method
651% method to read and/or write the format, whether the format supports the
652% saving of more than one frame to the same file or blob, whether the format
653% supports native in-memory I/O, and a brief description of the format.
654%
655% The format of the RegisterJP2Image method is:
656%
cristybb503372010-05-27 20:51:26 +0000657% size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000658%
659*/
cristybb503372010-05-27 20:51:26 +0000660ModuleExport size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000661{
662 MagickInfo
663 *entry;
664
665 entry=SetMagickInfo("JP2");
666 entry->description=ConstantString("JPEG-2000 File Format Syntax");
667 entry->module=ConstantString("JP2");
668 entry->magick=(IsImageFormatHandler *) IsJP2;
669 entry->adjoin=MagickFalse;
670 entry->seekable_stream=MagickTrue;
671 entry->thread_support=NoThreadSupport;
672#if defined(MAGICKCORE_JP2_DELEGATE)
673 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
674 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
675#endif
676 (void) RegisterMagickInfo(entry);
677 entry=SetMagickInfo("JPC");
678 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000679 entry->module=ConstantString("JP2");
680 entry->magick=(IsImageFormatHandler *) IsJPC;
681 entry->adjoin=MagickFalse;
682 entry->seekable_stream=MagickTrue;
683 entry->thread_support=NoThreadSupport;
684#if defined(MAGICKCORE_JP2_DELEGATE)
685 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
686 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
687#endif
688 (void) RegisterMagickInfo(entry);
689 entry=SetMagickInfo("J2C");
690 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
691 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000692 entry->magick=(IsImageFormatHandler *) IsJPC;
693 entry->adjoin=MagickFalse;
694 entry->seekable_stream=MagickTrue;
695 entry->thread_support=NoThreadSupport;
696#if defined(MAGICKCORE_JP2_DELEGATE)
697 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
698 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
699#endif
700 (void) RegisterMagickInfo(entry);
701 entry=SetMagickInfo("JPX");
702 entry->description=ConstantString("JPEG-2000 File Format Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000703 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000704 entry->magick=(IsImageFormatHandler *) IsJPC;
705 entry->adjoin=MagickFalse;
706 entry->seekable_stream=MagickTrue;
707 entry->thread_support=NoThreadSupport;
708#if defined(MAGICKCORE_JP2_DELEGATE)
709 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
710 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
711#endif
712 (void) RegisterMagickInfo(entry);
713 entry=SetMagickInfo("PGX");
714 entry->description=ConstantString("JPEG-2000 VM Format");
cristy02f1fda2009-12-10 15:23:56 +0000715 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000716 entry->magick=(IsImageFormatHandler *) IsJPC;
717 entry->adjoin=MagickFalse;
718 entry->seekable_stream=MagickTrue;
719 entry->thread_support=NoThreadSupport;
720#if defined(MAGICKCORE_JP2_DELEGATE)
721 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
722#endif
723 (void) RegisterMagickInfo(entry);
724#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000725 if (instantiate_jp2 == MagickFalse)
726 {
727 jas_init();
728 instantiate_jp2=MagickTrue;
729 }
cristy3ed852e2009-09-05 21:47:34 +0000730#endif
731 return(MagickImageCoderSignature);
732}
733
734/*
735%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736% %
737% %
738% %
739% U n r e g i s t e r J P 2 I m a g e %
740% %
741% %
742% %
743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
744%
745% UnregisterJP2Image() removes format registrations made by the JP2 module
746% from the list of supported formats.
747%
748% The format of the UnregisterJP2Image method is:
749%
750% UnregisterJP2Image(void)
751%
752*/
753ModuleExport void UnregisterJP2Image(void)
754{
cristy3ed852e2009-09-05 21:47:34 +0000755 (void) UnregisterMagickInfo("PGX");
cristy02f1fda2009-12-10 15:23:56 +0000756 (void) UnregisterMagickInfo("J2C");
757 (void) UnregisterMagickInfo("JPC");
758 (void) UnregisterMagickInfo("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000759#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000760 if (instantiate_jp2 != MagickFalse)
761 {
cristy992729d2010-01-01 15:45:06 +0000762 jas_cleanup();
cristy1e09ca22009-12-27 18:04:58 +0000763 instantiate_jp2=MagickFalse;
764 }
cristy3ed852e2009-09-05 21:47:34 +0000765#endif
766}
767
768#if defined(MAGICKCORE_JP2_DELEGATE)
769/*
770%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
771% %
772% %
773% %
774% W r i t e J P 2 I m a g e %
775% %
776% %
777% %
778%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
779%
780% WriteJP2Image() writes an image in the JPEG 2000 image format.
781%
782% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
783%
784% The format of the WriteJP2Image method is:
785%
786% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
787%
788% A description of each parameter follows.
789%
790% o image_info: the image info.
791%
792% o image: The image.
793%
794*/
795static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
796{
797 char
798 *key,
799 magick[MaxTextExtent],
800 *options;
801
802 const char
803 *option;
804
cristybb503372010-05-27 20:51:26 +0000805 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000806 format,
807 y;
808
809 jas_image_cmptparm_t
810 component_info[4];
811
812 jas_image_t
813 *jp2_image;
814
815 jas_matrix_t
816 *pixels[4];
817
818 jas_stream_t
819 *jp2_stream;
820
821 MagickBooleanType
822 status;
823
cristy18f8ee42009-09-30 01:04:36 +0000824 QuantumAny
825 range;
826
cristy3ed852e2009-09-05 21:47:34 +0000827 register const PixelPacket
828 *p;
829
cristybb503372010-05-27 20:51:26 +0000830 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000831 i,
832 x;
833
cristybb503372010-05-27 20:51:26 +0000834 size_t
cristy3ed852e2009-09-05 21:47:34 +0000835 number_components;
836
837 /*
838 Open image file.
839 */
840 assert(image_info != (const ImageInfo *) NULL);
841 assert(image_info->signature == MagickSignature);
842 assert(image != (Image *) NULL);
843 assert(image->signature == MagickSignature);
844 if (image->debug != MagickFalse)
845 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
846 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
847 if (status == MagickFalse)
848 return(status);
849 /*
850 Intialize JPEG 2000 API.
851 */
852 if (image->colorspace != RGBColorspace)
853 (void) TransformImageColorspace(image,RGBColorspace);
854 jp2_stream=JP2StreamManager(image);
855 if (jp2_stream == (jas_stream_t *) NULL)
856 ThrowWriterException(DelegateError,"UnableToManageJP2Stream");
857 number_components=image->matte ? 4UL : 3UL;
858 if ((image_info->type != TrueColorType) &&
cristy42a6bc82010-04-16 15:52:04 +0000859 (IsGrayImage(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000860 number_components=1;
861 if ((image->columns != (unsigned int) image->columns) ||
862 (image->rows != (unsigned int) image->rows))
863 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
cristy30faca22009-09-29 13:49:52 +0000864 (void) ResetMagickMemory(&component_info,0,sizeof(component_info));
cristybb503372010-05-27 20:51:26 +0000865 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000866 {
867 component_info[i].tlx=0;
868 component_info[i].tly=0;
869 component_info[i].hstep=1;
870 component_info[i].vstep=1;
871 component_info[i].width=(unsigned int) image->columns;
872 component_info[i].height=(unsigned int) image->rows;
cristy30faca22009-09-29 13:49:52 +0000873 component_info[i].prec=(int) MagickMax(MagickMin(image->depth,16),2);
cristy3ed852e2009-09-05 21:47:34 +0000874 component_info[i].sgnd=MagickFalse;
875 }
876 jp2_image=jas_image_create((int) number_components,component_info,
877 JAS_CLRSPC_UNKNOWN);
878 if (jp2_image == (jas_image_t *) NULL)
879 ThrowWriterException(DelegateError,"UnableToCreateImage");
880 if (number_components == 1)
881 {
882 /*
883 sRGB Grayscale.
884 */
885 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SGRAY);
886 jas_image_setcmpttype(jp2_image,0,
887 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
888 }
889 else
890 {
891 /*
892 sRGB.
893 */
cristy3ed852e2009-09-05 21:47:34 +0000894 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SRGB);
895 jas_image_setcmpttype(jp2_image,0,
cristy722af032009-12-04 01:50:06 +0000896 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
cristy3ed852e2009-09-05 21:47:34 +0000897 jas_image_setcmpttype(jp2_image,1,
cristy722af032009-12-04 01:50:06 +0000898 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
cristy3ed852e2009-09-05 21:47:34 +0000899 jas_image_setcmpttype(jp2_image,2,
cristy722af032009-12-04 01:50:06 +0000900 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
cristy30faca22009-09-29 13:49:52 +0000901 if (number_components == 4)
902 jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY);
cristy3ed852e2009-09-05 21:47:34 +0000903 }
904 /*
905 Convert to JPEG 2000 pixels.
906 */
cristybb503372010-05-27 20:51:26 +0000907 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000908 {
909 pixels[i]=jas_matrix_create(1,(int) image->columns);
910 if (pixels[i] == (jas_matrix_t *) NULL)
911 {
912 for (x=0; x < i; x++)
913 jas_matrix_destroy(pixels[x]);
914 jas_image_destroy(jp2_image);
915 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
916 }
917 }
cristybb503372010-05-27 20:51:26 +0000918 range=GetQuantumRange((size_t) component_info[0].prec);
919 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000920 {
921 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
922 if (p == (const PixelPacket *) NULL)
923 break;
cristybb503372010-05-27 20:51:26 +0000924 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000925 {
926 if (number_components == 1)
cristy722af032009-12-04 01:50:06 +0000927 jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny(
928 PixelIntensityToQuantum(p),range));
cristy3ed852e2009-09-05 21:47:34 +0000929 else
930 {
cristy722af032009-12-04 01:50:06 +0000931 jas_matrix_setv(pixels[0],x,(jas_seqent_t)
932 ScaleQuantumToAny(p->red,range));
933 jas_matrix_setv(pixels[1],x,(jas_seqent_t)
934 ScaleQuantumToAny(p->green,range));
935 jas_matrix_setv(pixels[2],x,(jas_seqent_t)
936 ScaleQuantumToAny(p->blue,range));
cristy3ed852e2009-09-05 21:47:34 +0000937 if (number_components > 3)
cristy722af032009-12-04 01:50:06 +0000938 jas_matrix_setv(pixels[3],x,(jas_seqent_t)
cristy46f08202010-01-10 04:04:21 +0000939 ScaleQuantumToAny((Quantum) (GetAlphaPixelComponent(p)),range));
cristy3ed852e2009-09-05 21:47:34 +0000940 }
941 p++;
942 }
cristybb503372010-05-27 20:51:26 +0000943 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000944 (void) jas_image_writecmpt(jp2_image,(short) i,0,(unsigned int) y,
945 (unsigned int) image->columns,1,pixels[i]);
cristycee97112010-05-28 00:44:52 +0000946 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
947 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000948 if (status == MagickFalse)
949 break;
950 }
951 (void) CopyMagickString(magick,image_info->magick,MaxTextExtent);
cristy02f1fda2009-12-10 15:23:56 +0000952 if (LocaleCompare(magick,"J2C") == 0)
953 (void) CopyMagickString(magick,"JPC",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +0000954 LocaleLower(magick);
955 format=jas_image_strtofmt(magick);
956 options=(char *) NULL;
957 ResetImageOptionIterator(image_info);
958 key=GetNextImageOption(image_info);
cristy722af032009-12-04 01:50:06 +0000959 for ( ; key != (char *) NULL; key=GetNextImageOption(image_info))
cristy3ed852e2009-09-05 21:47:34 +0000960 {
961 option=GetImageOption(image_info,key);
cristy722af032009-12-04 01:50:06 +0000962 if (option == (const char *) NULL)
963 continue;
964 if (LocaleNCompare(key,"jp2:",4) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000965 {
cristy722af032009-12-04 01:50:06 +0000966 (void) ConcatenateString(&options,key+4);
967 if (*option != '\0')
cristy3ed852e2009-09-05 21:47:34 +0000968 {
cristy722af032009-12-04 01:50:06 +0000969 (void) ConcatenateString(&options,"=");
970 (void) ConcatenateString(&options,option);
cristy3ed852e2009-09-05 21:47:34 +0000971 }
cristy722af032009-12-04 01:50:06 +0000972 (void) ConcatenateString(&options," ");
973 }
cristy3ed852e2009-09-05 21:47:34 +0000974 }
975 option=GetImageOption(image_info,"jp2:rate");
976 if ((option == (const char *) NULL) &&
977 (image_info->compression != LosslessJPEGCompression) &&
978 (image->quality != UndefinedCompressionQuality) &&
cristy30faca22009-09-29 13:49:52 +0000979 ((double) image->quality <= 99.5) &&
980 ((image->rows*image->columns) > 2500))
cristy3ed852e2009-09-05 21:47:34 +0000981 {
982 char
983 option[MaxTextExtent];
984
985 double
986 alpha,
987 header_size,
988 number_pixels,
989 rate,
990 target_size;
991
992 alpha=115.0-image->quality;
993 rate=100.0/(alpha*alpha);
994 header_size=550.0;
995 header_size+=(number_components-1)*142;
996 number_pixels=(double) image->rows*image->columns*number_components*
997 (GetImageQuantumDepth(image,MagickTrue)/8);
998 target_size=(number_pixels*rate)+header_size;
999 rate=target_size/number_pixels;
cristye7f51092010-01-17 00:39:37 +00001000 (void) FormatMagickString(option,MaxTextExtent,"rate=%g",rate);
cristy3ed852e2009-09-05 21:47:34 +00001001 (void) ConcatenateString(&options,option);
1002 }
1003 status=jas_image_encode(jp2_image,jp2_stream,format,options) != 0 ?
1004 MagickTrue : MagickFalse;
1005 (void) jas_stream_close(jp2_stream);
cristybb503372010-05-27 20:51:26 +00001006 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +00001007 jas_matrix_destroy(pixels[i]);
1008 jas_image_destroy(jp2_image);
1009 if (status != MagickFalse)
1010 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1011 return(MagickTrue);
1012}
1013#endif