blob: bea364ffa7e3dfd254db479fa720ddd130c77e92 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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"
cristy510d06a2011-07-06 23:43:54 +000048#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/exception.h"
52#include "MagickCore/exception-private.h"
53#include "MagickCore/image.h"
54#include "MagickCore/image-private.h"
55#include "MagickCore/list.h"
56#include "MagickCore/magick.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/monitor.h"
59#include "MagickCore/monitor-private.h"
60#include "MagickCore/option.h"
61#include "MagickCore/pixel-accessor.h"
62#include "MagickCore/profile.h"
63#include "MagickCore/quantum-private.h"
64#include "MagickCore/static.h"
65#include "MagickCore/statistic.h"
66#include "MagickCore/string_.h"
67#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000068#if defined(MAGICKCORE_JP2_DELEGATE)
69#ifndef JAS_IMAGE_CM_GRAY
70#define JAS_IMAGE_CM_GRAY JAS_IMAGE_CS_GRAY
71#endif
72#ifndef JAS_IMAGE_CM_RGB
73#define JAS_IMAGE_CM_RGB JAS_IMAGE_CS_RGB
74#endif
75#if !defined(uchar)
76#define uchar unsigned char
77#endif
78#if !defined(ushort)
79#define ushort unsigned short
80#endif
81#if !defined(uint)
82#define uint unsigned int
83#endif
cristybb503372010-05-27 20:51:26 +000084#if !defined(ssize_tssize_t)
85#define ssize_tssize_t long long
cristy3ed852e2009-09-05 21:47:34 +000086#endif
cristybb503372010-05-27 20:51:26 +000087#if !defined(ussize_tssize_t)
88#define ussize_tssize_t unsigned long long
cristy3ed852e2009-09-05 21:47:34 +000089#endif
90
cristy30faca22009-09-29 13:49:52 +000091#undef PACKAGE_NAME
92#undef PACKAGE_STRING
93#undef PACKAGE_TARNAME
94#undef PACKAGE_VERSION
cristy3ed852e2009-09-05 21:47:34 +000095#include "jasper/jasper.h"
cristy30faca22009-09-29 13:49:52 +000096#undef PACKAGE_NAME
97#undef PACKAGE_STRING
98#undef PACKAGE_TARNAME
99#undef PACKAGE_VERSION
100
cristy3ed852e2009-09-05 21:47:34 +0000101#endif
102
103/*
104 Forward declarations.
105*/
106#if defined(MAGICKCORE_JP2_DELEGATE)
107static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000108 WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *);
cristy1e09ca22009-12-27 18:04:58 +0000109
110static volatile MagickBooleanType
111 instantiate_jp2 = MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000112#endif
113
114/*
115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116% %
117% %
118% %
119% I s J P 2 %
120% %
121% %
122% %
123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124%
125% IsJP2() returns MagickTrue if the image format type, identified by the
126% magick string, is JP2.
127%
128% The format of the IsJP2 method is:
129%
130% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
131%
132% A description of each parameter follows:
133%
134% o magick: compare image format pattern against these bytes.
135%
136% o length: Specifies the length of the magick string.
137%
138*/
139static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
140{
141 if (length < 9)
142 return(MagickFalse);
143 if (memcmp(magick+4,"\152\120\040\040\015",5) == 0)
144 return(MagickTrue);
145 return(MagickFalse);
146}
147
148/*
149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150% %
151% %
152% %
153% I s J P C %
154% %
155% %
156% %
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158%
159% IsJPC()() returns MagickTrue if the image format type, identified by the
160% magick string, is JPC.
161%
162% The format of the IsJPC method is:
163%
164% MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
165%
166% A description of each parameter follows:
167%
168% o magick: compare image format pattern against these bytes.
169%
170% o length: Specifies the length of the magick string.
171%
172*/
173static MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
174{
175 if (length < 2)
176 return(MagickFalse);
177 if (memcmp(magick,"\377\117",2) == 0)
178 return(MagickTrue);
179 return(MagickFalse);
180}
181
182/*
183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
184% %
185% %
186% %
187% R e a d J P 2 I m a g e %
188% %
189% %
190% %
191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192%
193% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
194% codestream (JPC) image file and returns it. It allocates the memory
195% necessary for the new Image structure and returns a pointer to the new
196% image or set of images.
197%
198% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
199%
200% The format of the ReadJP2Image method is:
201%
202% Image *ReadJP2Image(const ImageInfo *image_info,
203% ExceptionInfo *exception)
204%
205% A description of each parameter follows:
206%
207% o image_info: the image info.
208%
209% o exception: return any errors or warnings in this structure.
210%
211*/
212#if defined(MAGICKCORE_JP2_DELEGATE)
213
214typedef struct _StreamManager
215{
216 jas_stream_t
217 *stream;
218
219 Image
220 *image;
221} StreamManager;
222
223static int BlobRead(jas_stream_obj_t *object,char *buffer,const int length)
224{
225 ssize_t
226 count;
227
228 StreamManager
229 *source;
230
231 source=(StreamManager *) object;
232 count=ReadBlob(source->image,(size_t) length,(unsigned char *) buffer);
233 return((int) count);
234}
235
236static int BlobWrite(jas_stream_obj_t *object,char *buffer,const int length)
237{
238 ssize_t
239 count;
240
241 StreamManager
242 *source;
243
244 source=(StreamManager *) object;
245 count=WriteBlob(source->image,(size_t) length,(unsigned char *) buffer);
246 return((int) count);
247}
248
cristyf1d91242010-05-28 02:23:19 +0000249static long BlobSeek(jas_stream_obj_t *object,long offset,int origin)
cristy3ed852e2009-09-05 21:47:34 +0000250{
251 StreamManager
252 *source;
253
254 source=(StreamManager *) object;
cristyf1d91242010-05-28 02:23:19 +0000255 return((long) SeekBlob(source->image,offset,origin));
cristy3ed852e2009-09-05 21:47:34 +0000256}
257
258static int BlobClose(jas_stream_obj_t *object)
259{
260 StreamManager
261 *source;
262
263 source=(StreamManager *) object;
264 (void) CloseBlob(source->image);
cristyb41ee102010-10-04 16:46:15 +0000265 free(source);
266 source=(StreamManager *) NULL;
cristy3ed852e2009-09-05 21:47:34 +0000267 return(0);
268}
269
270static inline size_t MagickMax(const size_t x,const size_t y)
271{
272 if (x > y)
273 return(x);
274 return(y);
275}
276
277static inline size_t MagickMin(const size_t x,const size_t y)
278{
279 if (x < y)
280 return(x);
281 return(y);
282}
283
284static jas_stream_t *JP2StreamManager(Image *image)
285{
286 static jas_stream_ops_t
287 StreamOperators =
288 {
289 BlobRead,
290 BlobWrite,
291 BlobSeek,
292 BlobClose
293 };
294
295 jas_stream_t
296 *stream;
297
298 StreamManager
299 *source;
300
301 stream=(jas_stream_t *) jas_malloc(sizeof(*stream));
302 if (stream == (jas_stream_t *) NULL)
303 return((jas_stream_t *) NULL);
304 (void) ResetMagickMemory(stream,0,sizeof(*stream));
305 stream->rwlimit_=(-1);
306 stream->obj_=(jas_stream_obj_t *) jas_malloc(sizeof(StreamManager));
307 if (stream->obj_ == (jas_stream_obj_t *) NULL)
308 return((jas_stream_t *) NULL);
309 (void) ResetMagickMemory(stream->obj_,0,sizeof(StreamManager));
310 stream->ops_=(&StreamOperators);
311 stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY;
312 stream->bufbase_=(unsigned char *) jas_malloc(JAS_STREAM_BUFSIZE+
313 JAS_STREAM_MAXPUTBACK);
314 if (stream->bufbase_ == (void *) NULL)
315 {
316 stream->bufbase_=stream->tinybuf_;
317 stream->bufsize_=1;
318 }
319 else
320 {
321 stream->bufmode_=JAS_STREAM_FREEBUF | JAS_STREAM_BUFMODEMASK;
322 stream->bufsize_=JAS_STREAM_BUFSIZE;
323 }
324 stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]);
325 stream->ptr_=stream->bufstart_;
326 stream->cnt_=0;
327 source=(StreamManager *) stream->obj_;
328 source->image=image;
329 return(stream);
330}
331
332static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
333{
334 Image
335 *image;
336
cristy3ed852e2009-09-05 21:47:34 +0000337 jas_cmprof_t
338 *cm_profile;
339
340 jas_iccprof_t
341 *icc_profile;
342
343 jas_image_t
344 *jp2_image;
345
346 jas_matrix_t
347 *pixels[4];
348
349 jas_stream_t
350 *jp2_stream;
351
352 MagickBooleanType
353 status;
354
355 QuantumAny
cristy30faca22009-09-29 13:49:52 +0000356 pixel,
cristy722af032009-12-04 01:50:06 +0000357 range[4];
cristy3ed852e2009-09-05 21:47:34 +0000358
cristybb503372010-05-27 20:51:26 +0000359 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000360 i,
361 x;
362
cristy4c08aed2011-07-01 19:47:50 +0000363 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000364 *q;
365
cristybb503372010-05-27 20:51:26 +0000366 size_t
cristy3ed852e2009-09-05 21:47:34 +0000367 maximum_component_depth,
368 number_components,
cristy3ed852e2009-09-05 21:47:34 +0000369 x_step[4],
370 y_step[4];
371
cristy524222d2011-04-25 00:37:06 +0000372 ssize_t
373 components[4],
374 y;
375
cristy3ed852e2009-09-05 21:47:34 +0000376 /*
377 Open image file.
378 */
379 assert(image_info != (const ImageInfo *) NULL);
380 assert(image_info->signature == MagickSignature);
381 if (image_info->debug != MagickFalse)
382 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
383 image_info->filename);
384 assert(exception != (ExceptionInfo *) NULL);
385 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000386 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000387 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
388 if (status == MagickFalse)
389 {
390 image=DestroyImageList(image);
391 return((Image *) NULL);
392 }
393 /*
394 Initialize JPEG 2000 API.
395 */
396 jp2_stream=JP2StreamManager(image);
397 if (jp2_stream == (jas_stream_t *) NULL)
398 ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
399 jp2_image=jas_image_decode(jp2_stream,-1,0);
400 if (jp2_image == (jas_image_t *) NULL)
401 {
402 (void) jas_stream_close(jp2_stream);
403 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
404 }
cristy2f040d02012-05-24 18:14:16 +0000405 image->columns=jas_image_width(jp2_image);
406 image->rows=jas_image_height(jp2_image);
407 image->compression=JPEG2000Compression;
cristy3ed852e2009-09-05 21:47:34 +0000408 switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
409 {
410 case JAS_CLRSPC_FAM_RGB:
411 {
cristy2f040d02012-05-24 18:14:16 +0000412 SetImageColorspace(image,RGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000413 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_R);
414 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_G);
415 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_RGB_B);
416 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
417 {
418 (void) jas_stream_close(jp2_stream);
419 jas_image_destroy(jp2_image);
420 ThrowReaderException(CorruptImageError,"MissingImageChannel");
421 }
422 number_components=3;
423 components[3]=jas_image_getcmptbytype(jp2_image,3);
424 if (components[3] > 0)
425 {
426 image->matte=MagickTrue;
427 number_components++;
428 }
429 break;
430 }
431 case JAS_CLRSPC_FAM_GRAY:
432 {
cristy2f040d02012-05-24 18:14:16 +0000433 SetImageColorspace(image,GRAYColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000434 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_GRAY_Y);
435 if (components[0] < 0)
436 {
437 (void) jas_stream_close(jp2_stream);
438 jas_image_destroy(jp2_image);
439 ThrowReaderException(CorruptImageError,"MissingImageChannel");
440 }
441 number_components=1;
442 break;
443 }
444 case JAS_CLRSPC_FAM_YCBCR:
445 {
cristy2f040d02012-05-24 18:14:16 +0000446 SetImageColorspace(image,YCbCrColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000447 components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_Y);
448 components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CB);
449 components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CR);
450 if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
451 {
452 (void) jas_stream_close(jp2_stream);
453 jas_image_destroy(jp2_image);
454 ThrowReaderException(CorruptImageError,"MissingImageChannel");
455 }
456 number_components=3;
457 components[3]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_UNKNOWN);
458 if (components[3] > 0)
459 {
460 image->matte=MagickTrue;
461 number_components++;
462 }
cristy3ed852e2009-09-05 21:47:34 +0000463 break;
464 }
465 default:
466 {
467 (void) jas_stream_close(jp2_stream);
468 jas_image_destroy(jp2_image);
469 ThrowReaderException(CoderError,"ColorspaceModelIsNotSupported");
470 }
471 }
cristybb503372010-05-27 20:51:26 +0000472 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000473 {
cristybb503372010-05-27 20:51:26 +0000474 size_t
cristy3ed852e2009-09-05 21:47:34 +0000475 height,
476 width;
477
cristybb503372010-05-27 20:51:26 +0000478 width=(size_t) (jas_image_cmptwidth(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000479 jas_image_cmpthstep(jp2_image,components[i]));
cristybb503372010-05-27 20:51:26 +0000480 height=(size_t) (jas_image_cmptheight(jp2_image,components[i])*
cristy3ed852e2009-09-05 21:47:34 +0000481 jas_image_cmptvstep(jp2_image,components[i]));
cristy30faca22009-09-29 13:49:52 +0000482 x_step[i]=(unsigned int) jas_image_cmpthstep(jp2_image,components[i]);
483 y_step[i]=(unsigned int) jas_image_cmptvstep(jp2_image,components[i]);
cristy3ed852e2009-09-05 21:47:34 +0000484 if ((width != image->columns) || (height != image->rows) ||
485 (jas_image_cmpttlx(jp2_image,components[i]) != 0) ||
cristy30faca22009-09-29 13:49:52 +0000486 (jas_image_cmpttly(jp2_image,components[i]) != 0) ||
cristy30faca22009-09-29 13:49:52 +0000487 (jas_image_cmptsgnd(jp2_image,components[i]) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000488 {
489 (void) jas_stream_close(jp2_stream);
490 jas_image_destroy(jp2_image);
491 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported");
492 }
cristy3ed852e2009-09-05 21:47:34 +0000493 }
494 /*
495 Convert JPEG 2000 pixels.
496 */
497 image->matte=number_components > 3 ? MagickTrue : MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000498 maximum_component_depth=0;
cristybb503372010-05-27 20:51:26 +0000499 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000500 {
501 maximum_component_depth=(unsigned int) MagickMax((size_t)
502 jas_image_cmptprec(jp2_image,components[i]),(size_t)
503 maximum_component_depth);
504 pixels[i]=jas_matrix_create(1,(int) (image->columns/x_step[i]));
505 if (pixels[i] == (jas_matrix_t *) NULL)
506 {
cristy30faca22009-09-29 13:49:52 +0000507 for (--i; i >= 0; i--)
508 jas_matrix_destroy(pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000509 jas_image_destroy(jp2_image);
510 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
511 }
512 }
cristy30faca22009-09-29 13:49:52 +0000513 image->depth=maximum_component_depth;
514 if (image_info->ping != MagickFalse)
515 {
516 (void) jas_stream_close(jp2_stream);
517 jas_image_destroy(jp2_image);
518 return(GetFirstImageInList(image));
519 }
cristybb503372010-05-27 20:51:26 +0000520 for (i=0; i < (ssize_t) number_components; i++)
521 range[i]=GetQuantumRange((size_t) jas_image_cmptprec(jp2_image,
cristy3ed852e2009-09-05 21:47:34 +0000522 components[i]));
cristybb503372010-05-27 20:51:26 +0000523 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000524 {
525 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000526 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000527 break;
cristybb503372010-05-27 20:51:26 +0000528 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000529 (void) jas_image_readcmpt(jp2_image,(short) components[i],0,
cristyf6fe0a12010-05-30 00:44:47 +0000530 (jas_image_coord_t) (y/y_step[i]),(jas_image_coord_t) (image->columns/
531 x_step[i]),1,pixels[i]);
cristy3ed852e2009-09-05 21:47:34 +0000532 switch (number_components)
533 {
534 case 1:
535 {
536 /*
537 Grayscale.
538 */
cristybb503372010-05-27 20:51:26 +0000539 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000540 {
541 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristyaf6eb932012-01-01 01:22:59 +0000542 SetPixelGray(image,ScaleAnyToQuantum((QuantumAny) pixel,range[0]),q);
cristyed231572011-07-14 02:18:59 +0000543 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);
cristyed231572011-07-14 02:18:59 +0000560 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);
cristyed231572011-07-14 02:18:59 +0000579 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_);
cristy9631d122011-09-01 13:28:16 +0000619 profile=BlobToStringInfo(blob->buf_,blob->len_);
620 if (profile == (StringInfo *) NULL)
621 ThrowReaderException(CorruptImageError,"MemoryAllocationFailed");
cristy3ed852e2009-09-05 21:47:34 +0000622 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
623 if (icc_profile == (StringInfo *) NULL)
cristyd15e6592011-10-15 00:13:06 +0000624 (void) SetImageProfile(image,"icc",profile,exception);
cristy3ed852e2009-09-05 21:47:34 +0000625 else
626 (void) ConcatenateStringInfo(icc_profile,profile);
627 profile=DestroyStringInfo(profile);
628 (void) jas_stream_close(icc_stream);
629 }
630 }
631 (void) jas_stream_close(jp2_stream);
632 jas_image_destroy(jp2_image);
cristybb503372010-05-27 20:51:26 +0000633 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000634 jas_matrix_destroy(pixels[i]);
635 return(GetFirstImageInList(image));
636}
637#endif
638
639/*
640%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
641% %
642% %
643% %
644% R e g i s t e r J P 2 I m a g e %
645% %
646% %
647% %
648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649%
650% RegisterJP2Image() adds attributes for the JP2 image format to the list of
651% supported formats. The attributes include the image format tag, a method
652% method to read and/or write the format, whether the format supports the
653% saving of more than one frame to the same file or blob, whether the format
654% supports native in-memory I/O, and a brief description of the format.
655%
656% The format of the RegisterJP2Image method is:
657%
cristybb503372010-05-27 20:51:26 +0000658% size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000659%
660*/
cristybb503372010-05-27 20:51:26 +0000661ModuleExport size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000662{
663 MagickInfo
664 *entry;
665
666 entry=SetMagickInfo("JP2");
667 entry->description=ConstantString("JPEG-2000 File Format Syntax");
668 entry->module=ConstantString("JP2");
669 entry->magick=(IsImageFormatHandler *) IsJP2;
670 entry->adjoin=MagickFalse;
671 entry->seekable_stream=MagickTrue;
672 entry->thread_support=NoThreadSupport;
673#if defined(MAGICKCORE_JP2_DELEGATE)
674 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
675 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
676#endif
677 (void) RegisterMagickInfo(entry);
678 entry=SetMagickInfo("JPC");
679 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000680 entry->module=ConstantString("JP2");
681 entry->magick=(IsImageFormatHandler *) IsJPC;
682 entry->adjoin=MagickFalse;
683 entry->seekable_stream=MagickTrue;
684 entry->thread_support=NoThreadSupport;
685#if defined(MAGICKCORE_JP2_DELEGATE)
686 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
687 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
688#endif
689 (void) RegisterMagickInfo(entry);
690 entry=SetMagickInfo("J2C");
691 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
692 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000693 entry->magick=(IsImageFormatHandler *) IsJPC;
694 entry->adjoin=MagickFalse;
695 entry->seekable_stream=MagickTrue;
696 entry->thread_support=NoThreadSupport;
697#if defined(MAGICKCORE_JP2_DELEGATE)
698 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
699 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
700#endif
701 (void) RegisterMagickInfo(entry);
cristy9bfeb942012-05-10 12:07:32 +0000702 entry=SetMagickInfo("J2K");
703 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
704 entry->module=ConstantString("JP2");
705 entry->magick=(IsImageFormatHandler *) IsJPC;
706 entry->adjoin=MagickFalse;
707 entry->seekable_stream=MagickTrue;
708 entry->thread_support=NoThreadSupport;
709#if defined(MAGICKCORE_JP2_DELEGATE)
710 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
711 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
712#endif
713 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +0000714 entry=SetMagickInfo("JPX");
715 entry->description=ConstantString("JPEG-2000 File Format Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000716 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000717 entry->magick=(IsImageFormatHandler *) IsJPC;
718 entry->adjoin=MagickFalse;
719 entry->seekable_stream=MagickTrue;
720 entry->thread_support=NoThreadSupport;
721#if defined(MAGICKCORE_JP2_DELEGATE)
722 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
723 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
724#endif
725 (void) RegisterMagickInfo(entry);
726 entry=SetMagickInfo("PGX");
727 entry->description=ConstantString("JPEG-2000 VM Format");
cristy02f1fda2009-12-10 15:23:56 +0000728 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000729 entry->magick=(IsImageFormatHandler *) IsJPC;
730 entry->adjoin=MagickFalse;
731 entry->seekable_stream=MagickTrue;
732 entry->thread_support=NoThreadSupport;
733#if defined(MAGICKCORE_JP2_DELEGATE)
734 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
735#endif
736 (void) RegisterMagickInfo(entry);
737#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000738 if (instantiate_jp2 == MagickFalse)
739 {
740 jas_init();
741 instantiate_jp2=MagickTrue;
742 }
cristy3ed852e2009-09-05 21:47:34 +0000743#endif
744 return(MagickImageCoderSignature);
745}
746
747/*
748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749% %
750% %
751% %
752% U n r e g i s t e r J P 2 I m a g e %
753% %
754% %
755% %
756%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757%
758% UnregisterJP2Image() removes format registrations made by the JP2 module
759% from the list of supported formats.
760%
761% The format of the UnregisterJP2Image method is:
762%
763% UnregisterJP2Image(void)
764%
765*/
766ModuleExport void UnregisterJP2Image(void)
767{
cristy3ed852e2009-09-05 21:47:34 +0000768 (void) UnregisterMagickInfo("PGX");
cristy9bfeb942012-05-10 12:07:32 +0000769 (void) UnregisterMagickInfo("J2K");
cristy02f1fda2009-12-10 15:23:56 +0000770 (void) UnregisterMagickInfo("J2C");
771 (void) UnregisterMagickInfo("JPC");
772 (void) UnregisterMagickInfo("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000773#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000774 if (instantiate_jp2 != MagickFalse)
775 {
cristy992729d2010-01-01 15:45:06 +0000776 jas_cleanup();
cristy1e09ca22009-12-27 18:04:58 +0000777 instantiate_jp2=MagickFalse;
778 }
cristy3ed852e2009-09-05 21:47:34 +0000779#endif
780}
781
782#if defined(MAGICKCORE_JP2_DELEGATE)
783/*
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785% %
786% %
787% %
788% W r i t e J P 2 I m a g e %
789% %
790% %
791% %
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793%
794% WriteJP2Image() writes an image in the JPEG 2000 image format.
795%
796% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
797%
798% The format of the WriteJP2Image method is:
799%
cristy1e178e72011-08-28 19:44:34 +0000800% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,
801% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000802%
803% A description of each parameter follows.
804%
805% o image_info: the image info.
806%
807% o image: The image.
808%
cristy1e178e72011-08-28 19:44:34 +0000809% o exception: return any errors or warnings in this structure.
810%
cristy3ed852e2009-09-05 21:47:34 +0000811*/
cristy1e178e72011-08-28 19:44:34 +0000812static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
813 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000814{
815 char
816 *key,
817 magick[MaxTextExtent],
818 *options;
819
820 const char
821 *option;
822
cristy3ed852e2009-09-05 21:47:34 +0000823 jas_image_cmptparm_t
824 component_info[4];
825
826 jas_image_t
827 *jp2_image;
828
829 jas_matrix_t
830 *pixels[4];
831
832 jas_stream_t
833 *jp2_stream;
834
835 MagickBooleanType
836 status;
837
cristy18f8ee42009-09-30 01:04:36 +0000838 QuantumAny
839 range;
840
cristy4c08aed2011-07-01 19:47:50 +0000841 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000842 *p;
843
cristybb503372010-05-27 20:51:26 +0000844 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000845 i,
846 x;
847
cristybb503372010-05-27 20:51:26 +0000848 size_t
cristy3ed852e2009-09-05 21:47:34 +0000849 number_components;
850
cristy524222d2011-04-25 00:37:06 +0000851 ssize_t
852 format,
853 y;
854
cristy3ed852e2009-09-05 21:47:34 +0000855 /*
856 Open image file.
857 */
858 assert(image_info != (const ImageInfo *) NULL);
859 assert(image_info->signature == MagickSignature);
860 assert(image != (Image *) NULL);
861 assert(image->signature == MagickSignature);
862 if (image->debug != MagickFalse)
863 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000864 assert(exception != (ExceptionInfo *) NULL);
865 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +0000866 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000867 if (status == MagickFalse)
868 return(status);
869 /*
glennrpbca49a22011-07-01 12:18:22 +0000870 Initialize JPEG 2000 API.
cristy3ed852e2009-09-05 21:47:34 +0000871 */
cristy501c5592012-04-18 12:45:09 +0000872 if (IssRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000873 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000874 jp2_stream=JP2StreamManager(image);
875 if (jp2_stream == (jas_stream_t *) NULL)
876 ThrowWriterException(DelegateError,"UnableToManageJP2Stream");
877 number_components=image->matte ? 4UL : 3UL;
878 if ((image_info->type != TrueColorType) &&
cristy1e178e72011-08-28 19:44:34 +0000879 (IsImageGray(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000880 number_components=1;
881 if ((image->columns != (unsigned int) image->columns) ||
882 (image->rows != (unsigned int) image->rows))
883 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
cristy30faca22009-09-29 13:49:52 +0000884 (void) ResetMagickMemory(&component_info,0,sizeof(component_info));
cristybb503372010-05-27 20:51:26 +0000885 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000886 {
887 component_info[i].tlx=0;
888 component_info[i].tly=0;
889 component_info[i].hstep=1;
890 component_info[i].vstep=1;
891 component_info[i].width=(unsigned int) image->columns;
892 component_info[i].height=(unsigned int) image->rows;
cristy30faca22009-09-29 13:49:52 +0000893 component_info[i].prec=(int) MagickMax(MagickMin(image->depth,16),2);
cristy3ed852e2009-09-05 21:47:34 +0000894 component_info[i].sgnd=MagickFalse;
895 }
896 jp2_image=jas_image_create((int) number_components,component_info,
897 JAS_CLRSPC_UNKNOWN);
898 if (jp2_image == (jas_image_t *) NULL)
899 ThrowWriterException(DelegateError,"UnableToCreateImage");
900 if (number_components == 1)
901 {
902 /*
903 sRGB Grayscale.
904 */
905 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SGRAY);
906 jas_image_setcmpttype(jp2_image,0,
907 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
908 }
909 else
910 {
911 /*
912 sRGB.
913 */
cristy3ed852e2009-09-05 21:47:34 +0000914 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SRGB);
915 jas_image_setcmpttype(jp2_image,0,
cristy722af032009-12-04 01:50:06 +0000916 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
cristy3ed852e2009-09-05 21:47:34 +0000917 jas_image_setcmpttype(jp2_image,1,
cristy722af032009-12-04 01:50:06 +0000918 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
cristy3ed852e2009-09-05 21:47:34 +0000919 jas_image_setcmpttype(jp2_image,2,
cristy722af032009-12-04 01:50:06 +0000920 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
cristy30faca22009-09-29 13:49:52 +0000921 if (number_components == 4)
922 jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY);
cristy3ed852e2009-09-05 21:47:34 +0000923 }
924 /*
925 Convert to JPEG 2000 pixels.
926 */
cristybb503372010-05-27 20:51:26 +0000927 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000928 {
929 pixels[i]=jas_matrix_create(1,(int) image->columns);
930 if (pixels[i] == (jas_matrix_t *) NULL)
931 {
932 for (x=0; x < i; x++)
933 jas_matrix_destroy(pixels[x]);
934 jas_image_destroy(jp2_image);
935 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
936 }
937 }
cristybb503372010-05-27 20:51:26 +0000938 range=GetQuantumRange((size_t) component_info[0].prec);
939 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000940 {
cristy1e178e72011-08-28 19:44:34 +0000941 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000942 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000943 break;
cristybb503372010-05-27 20:51:26 +0000944 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000945 {
946 if (number_components == 1)
cristy722af032009-12-04 01:50:06 +0000947 jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny(
cristy4c08aed2011-07-01 19:47:50 +0000948 GetPixelIntensity(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000949 else
950 {
cristy4c08aed2011-07-01 19:47:50 +0000951 jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny(
952 GetPixelRed(image,p),range));
953 jas_matrix_setv(pixels[1],x,(jas_seqent_t) ScaleQuantumToAny(
954 GetPixelGreen(image,p),range));
955 jas_matrix_setv(pixels[2],x,(jas_seqent_t) ScaleQuantumToAny(
956 GetPixelBlue(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000957 if (number_components > 3)
cristy4c08aed2011-07-01 19:47:50 +0000958 jas_matrix_setv(pixels[3],x,(jas_seqent_t) ScaleQuantumToAny(
959 GetPixelAlpha(image,p),range));
cristy3ed852e2009-09-05 21:47:34 +0000960 }
cristyed231572011-07-14 02:18:59 +0000961 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000962 }
cristybb503372010-05-27 20:51:26 +0000963 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000964 (void) jas_image_writecmpt(jp2_image,(short) i,0,(unsigned int) y,
965 (unsigned int) image->columns,1,pixels[i]);
cristycee97112010-05-28 00:44:52 +0000966 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000967 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000968 if (status == MagickFalse)
969 break;
970 }
971 (void) CopyMagickString(magick,image_info->magick,MaxTextExtent);
cristy02f1fda2009-12-10 15:23:56 +0000972 if (LocaleCompare(magick,"J2C") == 0)
973 (void) CopyMagickString(magick,"JPC",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +0000974 LocaleLower(magick);
975 format=jas_image_strtofmt(magick);
976 options=(char *) NULL;
977 ResetImageOptionIterator(image_info);
978 key=GetNextImageOption(image_info);
cristy722af032009-12-04 01:50:06 +0000979 for ( ; key != (char *) NULL; key=GetNextImageOption(image_info))
cristy3ed852e2009-09-05 21:47:34 +0000980 {
981 option=GetImageOption(image_info,key);
cristy722af032009-12-04 01:50:06 +0000982 if (option == (const char *) NULL)
983 continue;
984 if (LocaleNCompare(key,"jp2:",4) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000985 {
cristy722af032009-12-04 01:50:06 +0000986 (void) ConcatenateString(&options,key+4);
987 if (*option != '\0')
cristy3ed852e2009-09-05 21:47:34 +0000988 {
cristy722af032009-12-04 01:50:06 +0000989 (void) ConcatenateString(&options,"=");
990 (void) ConcatenateString(&options,option);
cristy3ed852e2009-09-05 21:47:34 +0000991 }
cristy722af032009-12-04 01:50:06 +0000992 (void) ConcatenateString(&options," ");
993 }
cristy3ed852e2009-09-05 21:47:34 +0000994 }
995 option=GetImageOption(image_info,"jp2:rate");
996 if ((option == (const char *) NULL) &&
997 (image_info->compression != LosslessJPEGCompression) &&
998 (image->quality != UndefinedCompressionQuality) &&
cristy30faca22009-09-29 13:49:52 +0000999 ((double) image->quality <= 99.5) &&
1000 ((image->rows*image->columns) > 2500))
cristy3ed852e2009-09-05 21:47:34 +00001001 {
1002 char
1003 option[MaxTextExtent];
1004
1005 double
1006 alpha,
1007 header_size,
1008 number_pixels,
1009 rate,
1010 target_size;
1011
1012 alpha=115.0-image->quality;
1013 rate=100.0/(alpha*alpha);
1014 header_size=550.0;
1015 header_size+=(number_components-1)*142;
1016 number_pixels=(double) image->rows*image->columns*number_components*
1017 (GetImageQuantumDepth(image,MagickTrue)/8);
1018 target_size=(number_pixels*rate)+header_size;
1019 rate=target_size/number_pixels;
cristyb51dff52011-05-19 16:55:47 +00001020 (void) FormatLocaleString(option,MaxTextExtent,"rate=%g",rate);
cristy3ed852e2009-09-05 21:47:34 +00001021 (void) ConcatenateString(&options,option);
1022 }
1023 status=jas_image_encode(jp2_image,jp2_stream,format,options) != 0 ?
1024 MagickTrue : MagickFalse;
cristy3dfccb22011-12-28 21:47:20 +00001025 if (options != (char *) NULL)
1026 options=DestroyString(options);
cristy3ed852e2009-09-05 21:47:34 +00001027 (void) jas_stream_close(jp2_stream);
cristybb503372010-05-27 20:51:26 +00001028 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +00001029 jas_matrix_destroy(pixels[i]);
1030 jas_image_destroy(jp2_image);
1031 if (status != MagickFalse)
1032 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1033 return(MagickTrue);
1034}
1035#endif