blob: c22d97ed1c98c3920ff6b44cace1b9cb190973c8 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/colorspace.h"
48#include "MagickCore/color.h"
49#include "MagickCore/color-private.h"
50#include "MagickCore/exception.h"
51#include "MagickCore/exception-private.h"
52#include "MagickCore/image.h"
53#include "MagickCore/image-private.h"
54#include "MagickCore/list.h"
55#include "MagickCore/magick.h"
56#include "MagickCore/memory_.h"
57#include "MagickCore/monitor.h"
58#include "MagickCore/monitor-private.h"
59#include "MagickCore/option.h"
60#include "MagickCore/pixel-accessor.h"
61#include "MagickCore/profile.h"
62#include "MagickCore/quantum-private.h"
63#include "MagickCore/static.h"
64#include "MagickCore/statistic.h"
65#include "MagickCore/string_.h"
66#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000067#if defined(MAGICKCORE_JP2_DELEGATE)
68#ifndef JAS_IMAGE_CM_GRAY
69#define JAS_IMAGE_CM_GRAY JAS_IMAGE_CS_GRAY
70#endif
71#ifndef JAS_IMAGE_CM_RGB
72#define JAS_IMAGE_CM_RGB JAS_IMAGE_CS_RGB
73#endif
74#if !defined(uchar)
75#define uchar unsigned char
76#endif
77#if !defined(ushort)
78#define ushort unsigned short
79#endif
80#if !defined(uint)
81#define uint unsigned int
82#endif
cristybb503372010-05-27 20:51:26 +000083#if !defined(ssize_tssize_t)
84#define ssize_tssize_t long long
cristy3ed852e2009-09-05 21:47:34 +000085#endif
cristybb503372010-05-27 20:51:26 +000086#if !defined(ussize_tssize_t)
87#define ussize_tssize_t unsigned long long
cristy3ed852e2009-09-05 21:47:34 +000088#endif
89
cristy30faca22009-09-29 13:49:52 +000090#undef PACKAGE_NAME
91#undef PACKAGE_STRING
92#undef PACKAGE_TARNAME
93#undef PACKAGE_VERSION
cristy3ed852e2009-09-05 21:47:34 +000094#include "jasper/jasper.h"
cristy30faca22009-09-29 13:49:52 +000095#undef PACKAGE_NAME
96#undef PACKAGE_STRING
97#undef PACKAGE_TARNAME
98#undef PACKAGE_VERSION
99
cristy3ed852e2009-09-05 21:47:34 +0000100#endif
101
102/*
103 Forward declarations.
104*/
105#if defined(MAGICKCORE_JP2_DELEGATE)
106static MagickBooleanType
107 WriteJP2Image(const ImageInfo *,Image *);
cristy1e09ca22009-12-27 18:04:58 +0000108
109static volatile MagickBooleanType
110 instantiate_jp2 = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000111#endif
112
113/*
114%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
115% %
116% %
117% %
118% I s J P 2 %
119% %
120% %
121% %
122%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123%
124% IsJP2() returns MagickTrue if the image format type, identified by the
125% magick string, is JP2.
126%
127% The format of the IsJP2 method is:
128%
129% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
130%
131% A description of each parameter follows:
132%
133% o magick: compare image format pattern against these bytes.
134%
135% o length: Specifies the length of the magick string.
136%
137*/
138static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
139{
140 if (length < 9)
141 return(MagickFalse);
142 if (memcmp(magick+4,"\152\120\040\040\015",5) == 0)
143 return(MagickTrue);
144 return(MagickFalse);
145}
146
147/*
148%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149% %
150% %
151% %
152% I s J P C %
153% %
154% %
155% %
156%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157%
158% IsJPC()() returns MagickTrue if the image format type, identified by the
159% magick string, is JPC.
160%
161% The format of the IsJPC method is:
162%
163% MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
164%
165% A description of each parameter follows:
166%
167% o magick: compare image format pattern against these bytes.
168%
169% o length: Specifies the length of the magick string.
170%
171*/
172static MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
173{
174 if (length < 2)
175 return(MagickFalse);
176 if (memcmp(magick,"\377\117",2) == 0)
177 return(MagickTrue);
178 return(MagickFalse);
179}
180
181/*
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183% %
184% %
185% %
186% R e a d J P 2 I m a g e %
187% %
188% %
189% %
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191%
192% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
193% codestream (JPC) image file and returns it. It allocates the memory
194% necessary for the new Image structure and returns a pointer to the new
195% image or set of images.
196%
197% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
198%
199% The format of the ReadJP2Image method is:
200%
201% Image *ReadJP2Image(const ImageInfo *image_info,
202% ExceptionInfo *exception)
203%
204% A description of each parameter follows:
205%
206% o image_info: the image info.
207%
208% o exception: return any errors or warnings in this structure.
209%
210*/
211#if defined(MAGICKCORE_JP2_DELEGATE)
212
213typedef struct _StreamManager
214{
215 jas_stream_t
216 *stream;
217
218 Image
219 *image;
220} StreamManager;
221
222static int BlobRead(jas_stream_obj_t *object,char *buffer,const int length)
223{
224 ssize_t
225 count;
226
227 StreamManager
228 *source;
229
230 source=(StreamManager *) object;
231 count=ReadBlob(source->image,(size_t) length,(unsigned char *) buffer);
232 return((int) count);
233}
234
235static int BlobWrite(jas_stream_obj_t *object,char *buffer,const int length)
236{
237 ssize_t
238 count;
239
240 StreamManager
241 *source;
242
243 source=(StreamManager *) object;
244 count=WriteBlob(source->image,(size_t) length,(unsigned char *) buffer);
245 return((int) count);
246}
247
cristyf1d91242010-05-28 02:23:19 +0000248static long BlobSeek(jas_stream_obj_t *object,long offset,int origin)
cristy3ed852e2009-09-05 21:47:34 +0000249{
250 StreamManager
251 *source;
252
253 source=(StreamManager *) object;
cristyf1d91242010-05-28 02:23:19 +0000254 return((long) SeekBlob(source->image,offset,origin));
cristy3ed852e2009-09-05 21:47:34 +0000255}
256
257static int BlobClose(jas_stream_obj_t *object)
258{
259 StreamManager
260 *source;
261
262 source=(StreamManager *) object;
263 (void) CloseBlob(source->image);
cristyb41ee102010-10-04 16:46:15 +0000264 free(source);
265 source=(StreamManager *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000266 return(0);
267}
268
269static inline size_t MagickMax(const size_t x,const size_t y)
270{
271 if (x > y)
272 return(x);
273 return(y);
274}
275
276static inline size_t MagickMin(const size_t x,const size_t y)
277{
278 if (x < y)
279 return(x);
280 return(y);
281}
282
283static jas_stream_t *JP2StreamManager(Image *image)
284{
285 static jas_stream_ops_t
286 StreamOperators =
287 {
288 BlobRead,
289 BlobWrite,
290 BlobSeek,
291 BlobClose
292 };
293
294 jas_stream_t
295 *stream;
296
297 StreamManager
298 *source;
299
300 stream=(jas_stream_t *) jas_malloc(sizeof(*stream));
301 if (stream == (jas_stream_t *) NULL)
302 return((jas_stream_t *) NULL);
303 (void) ResetMagickMemory(stream,0,sizeof(*stream));
304 stream->rwlimit_=(-1);
305 stream->obj_=(jas_stream_obj_t *) jas_malloc(sizeof(StreamManager));
306 if (stream->obj_ == (jas_stream_obj_t *) NULL)
307 return((jas_stream_t *) NULL);
308 (void) ResetMagickMemory(stream->obj_,0,sizeof(StreamManager));
309 stream->ops_=(&StreamOperators);
310 stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
311 stream->bufbase_=(unsigned char *) jas_malloc(JAS_STREAM_BUFSIZE+
312 JAS_STREAM_MAXPUTBACK);
313 if (stream->bufbase_ == (void *) NULL)
314 {
315 stream->bufbase_=stream->tinybuf_;
316 stream->bufsize_=1;
317 }
318 else
319 {
320 stream->bufmode_=JAS_STREAM_FREEBUF | JAS_STREAM_BUFMODEMASK;
321 stream->bufsize_=JAS_STREAM_BUFSIZE;
322 }
323 stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
324 stream->ptr_=stream->bufstart_;
325 stream->cnt_=0;
326 source=(StreamManager *) stream->obj_;
327 source->image=image;
328 return(stream);
329}
330
331static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
332{
333 Image
334 *image;
335
cristy3ed852e2009-09-05 21:47:34 +0000336 jas_cmprof_t
337 *cm_profile;
338
339 jas_iccprof_t
340 *icc_profile;
341
342 jas_image_t
343 *jp2_image;
344
345 jas_matrix_t
346 *pixels[4];
347
348 jas_stream_t
349 *jp2_stream;
350
351 MagickBooleanType
352 status;
353
354 QuantumAny
cristy30faca22009-09-29 13:49:52 +0000355 pixel,
cristy722af032009-12-04 01:50:06 +0000356 range[4];
cristy3ed852e2009-09-05 21:47:34 +0000357
cristybb503372010-05-27 20:51:26 +0000358 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000359 i,
360 x;
361
cristy4c08aed2011-07-01 19:47:50 +0000362 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000363 *q;
364
cristybb503372010-05-27 20:51:26 +0000365 size_t
cristy3ed852e2009-09-05 21:47:34 +0000366 maximum_component_depth,
367 number_components,
cristy3ed852e2009-09-05 21:47:34 +0000368 x_step[4],
369 y_step[4];
370
cristy524222d2011-04-25 00:37:06 +0000371 ssize_t
372 components[4],
373 y;
374
cristy3ed852e2009-09-05 21:47:34 +0000375 /*
376 Open image file.
377 */
378 assert(image_info != (const ImageInfo *) NULL);
379 assert(image_info->signature == MagickSignature);
380 if (image_info->debug != MagickFalse)
381 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
382 image_info->filename);
383 assert(exception != (ExceptionInfo *) NULL);
384 assert(exception->signature == MagickSignature);
385 image=AcquireImage(image_info);
386 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
387 if (status == MagickFalse)
388 {
389 image=DestroyImageList(image);
390 return((Image *) NULL);
391 }
392 /*
393 Initialize JPEG 2000 API.
394 */
395 jp2_stream=JP2StreamManager(image);
396 if (jp2_stream == (jas_stream_t *) NULL)
397 ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
398 jp2_image=jas_image_decode(jp2_stream,-1,0);
399 if (jp2_image == (jas_image_t *) NULL)
400 {
401 (void) jas_stream_close(jp2_stream);
402 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
403 }
cristy3ed852e2009-09-05 21:47:34 +0000404 switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
405 {
406 case JAS_CLRSPC_FAM_RGB:
407 {
408 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_R);
409 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_G);
410 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_B);
411 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
412 {
413 (void) jas_stream_close(jp2_stream);
414 jas_image_destroy(jp2_image);
415 ThrowReaderException(CorruptImageError,"MissingImageChannel");
416 }
417 number_components=3;
418 components[3]=jas_image_getcmptbytype(jp2_image,3);
419 if (components[3] > 0)
420 {
421 image->matte=MagickTrue;
422 number_components++;
423 }
424 break;
425 }
426 case JAS_CLRSPC_FAM_GRAY:
427 {
428 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_GRAY_Y);
429 if (components[0] < 0)
430 {
431 (void) jas_stream_close(jp2_stream);
432 jas_image_destroy(jp2_image);
433 ThrowReaderException(CorruptImageError,"MissingImageChannel");
434 }
435 number_components=1;
436 break;
437 }
438 case JAS_CLRSPC_FAM_YCBCR:
439 {
440 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_Y);
441 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CB);
442 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CR);
443 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
444 {
445 (void) jas_stream_close(jp2_stream);
446 jas_image_destroy(jp2_image);
447 ThrowReaderException(CorruptImageError,"MissingImageChannel");
448 }
449 number_components=3;
450 components[3]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_UNKNOWN);
451 if (components[3] > 0)
452 {
453 image->matte=MagickTrue;
454 number_components++;
455 }
456 image->colorspace=YCbCrColorspace;
457 break;
458 }
459 default:
460 {
461 (void) jas_stream_close(jp2_stream);
462 jas_image_destroy(jp2_image);
463 ThrowReaderException(CoderError,"ColorspaceModelIsNotSupported");
464 }
465 }
cristy30faca22009-09-29 13:49:52 +0000466 image->columns=jas_image_width(jp2_image);
467 image->rows=jas_image_height(jp2_image);
cristy3ed852e2009-09-05 21:47:34 +0000468 image->compression=JPEG2000Compression;
cristybb503372010-05-27 20:51:26 +0000469 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000470 {
cristybb503372010-05-27 20:51:26 +0000471 size_t
cristy3ed852e2009-09-05 21:47:34 +0000472 height,
473 width;
474
cristybb503372010-05-27 20:51:26 +0000475 width=(size_t) (jas_image_cmptwidth(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000476 jas_image_cmpthstep(jp2_image,components[i]));
cristybb503372010-05-27 20:51:26 +0000477 height=(size_t) (jas_image_cmptheight(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000478 jas_image_cmptvstep(jp2_image,components[i]));
cristy30faca22009-09-29 13:49:52 +0000479 x_step[i]=(unsigned int) jas_image_cmpthstep(jp2_image,components[i]);
480 y_step[i]=(unsigned int) jas_image_cmptvstep(jp2_image,components[i]);
cristy3ed852e2009-09-05 21:47:34 +0000481 if ((width != image->columns) || (height != image->rows) ||
482 (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
cristy30faca22009-09-29 13:49:52 +0000483 (jas_image_cmpttly(jp2_image,components[i]) != 0) ||
484 (x_step[i] != 1) || (y_step[i] != 1) ||
485 (jas_image_cmptsgnd(jp2_image,components[i]) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000486 {
487 (void) jas_stream_close(jp2_stream);
488 jas_image_destroy(jp2_image);
489 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported");
490 }
cristy3ed852e2009-09-05 21:47:34 +0000491 }
492 /*
493 Convert JPEG 2000 pixels.
494 */
495 image->matte=number_components > 3 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000496 maximum_component_depth=0;
cristybb503372010-05-27 20:51:26 +0000497 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000498 {
499 maximum_component_depth=(unsigned int) MagickMax((size_t)
500 jas_image_cmptprec(jp2_image,components[i]),(size_t)
501 maximum_component_depth);
502 pixels[i]=jas_matrix_create(1,(int) (image->columns/x_step[i]));
503 if (pixels[i] == (jas_matrix_t *) NULL)
504 {
cristy30faca22009-09-29 13:49:52 +0000505 for (--i; i >= 0; i--)
506 jas_matrix_destroy(pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000507 jas_image_destroy(jp2_image);
508 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
509 }
510 }
cristy30faca22009-09-29 13:49:52 +0000511 image->depth=maximum_component_depth;
512 if (image_info->ping != MagickFalse)
513 {
514 (void) jas_stream_close(jp2_stream);
515 jas_image_destroy(jp2_image);
516 return(GetFirstImageInList(image));
517 }
cristybb503372010-05-27 20:51:26 +0000518 for (i=0; i < (ssize_t) number_components; i++)
519 range[i]=GetQuantumRange((size_t) jas_image_cmptprec(jp2_image,
cristy3ed852e2009-09-05 21:47:34 +0000520 components[i]));
cristybb503372010-05-27 20:51:26 +0000521 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000522 {
523 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000524 if (q == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000525 break;
cristybb503372010-05-27 20:51:26 +0000526 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000527 (void) jas_image_readcmpt(jp2_image,(short) components[i],0,
cristyf6fe0a12010-05-30 00:44:47 +0000528 (jas_image_coord_t) (y/y_step[i]),(jas_image_coord_t) (image->columns/
529 x_step[i]),1,pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000530 switch (number_components)
531 {
532 case 1:
533 {
534 /*
535 Grayscale.
536 */
cristybb503372010-05-27 20:51:26 +0000537 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000538 {
539 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy4c08aed2011-07-01 19:47:50 +0000540 SetPixelRed(image,ScaleAnyToQuantum((QuantumAny) pixel,range[0]),q);
541 SetPixelGreen(image,GetPixelRed(image,q),q);
542 SetPixelBlue(image,GetPixelRed(image,q),q);
543 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000544 }
545 break;
546 }
547 case 3:
548 {
549 /*
550 RGB.
551 */
cristybb503372010-05-27 20:51:26 +0000552 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000553 {
554 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy4c08aed2011-07-01 19:47:50 +0000555 SetPixelRed(image,ScaleAnyToQuantum((QuantumAny) pixel,range[0]),q);
cristy3ed852e2009-09-05 21:47:34 +0000556 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy4c08aed2011-07-01 19:47:50 +0000557 SetPixelGreen(image,ScaleAnyToQuantum((QuantumAny) pixel,range[1]),q);
cristy3ed852e2009-09-05 21:47:34 +0000558 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy4c08aed2011-07-01 19:47:50 +0000559 SetPixelBlue(image,ScaleAnyToQuantum((QuantumAny) pixel,range[2]),q);
560 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000561 }
562 break;
563 }
564 case 4:
565 {
566 /*
567 RGBA.
568 */
cristybb503372010-05-27 20:51:26 +0000569 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000570 {
571 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy4c08aed2011-07-01 19:47:50 +0000572 SetPixelRed(image,ScaleAnyToQuantum((QuantumAny) pixel,range[0]),q);
cristy3ed852e2009-09-05 21:47:34 +0000573 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy4c08aed2011-07-01 19:47:50 +0000574 SetPixelGreen(image,ScaleAnyToQuantum((QuantumAny) pixel,range[1]),q);
cristy3ed852e2009-09-05 21:47:34 +0000575 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy4c08aed2011-07-01 19:47:50 +0000576 SetPixelBlue(image,ScaleAnyToQuantum((QuantumAny) pixel,range[2]),q);
cristy3ed852e2009-09-05 21:47:34 +0000577 pixel=(QuantumAny) jas_matrix_getv(pixels[3],x/x_step[3]);
cristy4c08aed2011-07-01 19:47:50 +0000578 SetPixelAlpha(image,ScaleAnyToQuantum((QuantumAny) pixel,range[3]),q);
579 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000580 }
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,
cristy524222d2011-04-25 00:37:06 +0000587 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
cristy3ed852e2009-09-05 21:47:34 +0000805 jas_image_cmptparm_t
806 component_info[4];
807
808 jas_image_t
809 *jp2_image;
810
811 jas_matrix_t
812 *pixels[4];
813
814 jas_stream_t
815 *jp2_stream;
816
817 MagickBooleanType
818 status;
819
cristy18f8ee42009-09-30 01:04:36 +0000820 QuantumAny
821 range;
822
cristy4c08aed2011-07-01 19:47:50 +0000823 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000824 *p;
825
cristybb503372010-05-27 20:51:26 +0000826 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000827 i,
828 x;
829
cristybb503372010-05-27 20:51:26 +0000830 size_t
cristy3ed852e2009-09-05 21:47:34 +0000831 number_components;
832
cristy524222d2011-04-25 00:37:06 +0000833 ssize_t
834 format,
835 y;
836
cristy3ed852e2009-09-05 21:47:34 +0000837 /*
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 /*
glennrpbca49a22011-07-01 12:18:22 +0000850 Initialize JPEG 2000 API.
cristy3ed852e2009-09-05 21:47:34 +0000851 */
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) &&
cristy4c08aed2011-07-01 19:47:50 +0000859 (IsImageGray(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);
cristy4c08aed2011-07-01 19:47:50 +0000922 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000923 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(
cristy4c08aed2011-07-01 19:47:50 +0000928 GetPixelIntensity(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000929 else
930 {
cristy4c08aed2011-07-01 19:47:50 +0000931 jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny(
932 GetPixelRed(image,p),range));
933 jas_matrix_setv(pixels[1],x,(jas_seqent_t) ScaleQuantumToAny(
934 GetPixelGreen(image,p),range));
935 jas_matrix_setv(pixels[2],x,(jas_seqent_t) ScaleQuantumToAny(
936 GetPixelBlue(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000937 if (number_components > 3)
cristy4c08aed2011-07-01 19:47:50 +0000938 jas_matrix_setv(pixels[3],x,(jas_seqent_t) ScaleQuantumToAny(
939 GetPixelAlpha(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000940 }
cristy4c08aed2011-07-01 19:47:50 +0000941 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000942 }
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,
cristy524222d2011-04-25 00:37:06 +0000947 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;
cristyb51dff52011-05-19 16:55:47 +00001000 (void) FormatLocaleString(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