blob: c566d6fd6713225fac8ad2ee2db5d8ef52b16231 [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*/
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
350 MagickBooleanType
351 status;
352
353 QuantumAny
cristy30faca22009-09-29 13:49:52 +0000354 pixel,
cristy722af032009-12-04 01:50:06 +0000355 range[4];
cristy3ed852e2009-09-05 21:47:34 +0000356
cristybb503372010-05-27 20:51:26 +0000357 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000358 i,
359 x;
360
361 register PixelPacket
362 *q;
363
cristybb503372010-05-27 20:51:26 +0000364 size_t
cristy3ed852e2009-09-05 21:47:34 +0000365 maximum_component_depth,
366 number_components,
cristy3ed852e2009-09-05 21:47:34 +0000367 x_step[4],
368 y_step[4];
369
cristy524222d2011-04-25 00:37:06 +0000370 ssize_t
371 components[4],
372 y;
373
cristy3ed852e2009-09-05 21:47:34 +0000374 /*
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]);
cristy524222d2011-04-25 00:37:06 +0000539 SetRedPixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
540 range[0]));
cristy5fde9aa2011-04-23 01:31:53 +0000541 SetGreenPixelComponent(q,GetRedPixelComponent(q));
542 SetBluePixelComponent(q,GetRedPixelComponent(q));
cristy3ed852e2009-09-05 21:47:34 +0000543 q++;
544 }
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]);
cristy524222d2011-04-25 00:37:06 +0000555 SetRedPixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
556 range[0]));
cristy3ed852e2009-09-05 21:47:34 +0000557 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy524222d2011-04-25 00:37:06 +0000558 SetGreenPixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
559 range[1]));
cristy3ed852e2009-09-05 21:47:34 +0000560 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy524222d2011-04-25 00:37:06 +0000561 SetBluePixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
562 range[2]));
cristy3ed852e2009-09-05 21:47:34 +0000563 q++;
564 }
565 break;
566 }
567 case 4:
568 {
569 /*
570 RGBA.
571 */
cristybb503372010-05-27 20:51:26 +0000572 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000573 {
574 pixel=(QuantumAny) jas_matrix_getv(pixels[0],x/x_step[0]);
cristy524222d2011-04-25 00:37:06 +0000575 SetRedPixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
576 range[0]));
cristy3ed852e2009-09-05 21:47:34 +0000577 pixel=(QuantumAny) jas_matrix_getv(pixels[1],x/x_step[1]);
cristy524222d2011-04-25 00:37:06 +0000578 SetGreenPixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
579 range[1]));
cristy3ed852e2009-09-05 21:47:34 +0000580 pixel=(QuantumAny) jas_matrix_getv(pixels[2],x/x_step[2]);
cristy524222d2011-04-25 00:37:06 +0000581 SetBluePixelComponent(q,ScaleAnyToQuantum((QuantumAny) pixel,
582 range[2]));
cristy3ed852e2009-09-05 21:47:34 +0000583 pixel=(QuantumAny) jas_matrix_getv(pixels[3],x/x_step[3]);
cristy524222d2011-04-25 00:37:06 +0000584 SetOpacityPixelComponent(q,(QuantumRange-
585 ScaleAnyToQuantum((QuantumAny) pixel,range[3])));
cristy3ed852e2009-09-05 21:47:34 +0000586 q++;
587 }
588 break;
589 }
590 }
591 if (SyncAuthenticPixels(image,exception) == MagickFalse)
592 break;
cristycee97112010-05-28 00:44:52 +0000593 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000594 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000595 if (status == MagickFalse)
596 break;
597 }
598 cm_profile=jas_image_cmprof(jp2_image);
599 icc_profile=(jas_iccprof_t *) NULL;
600 if (cm_profile != (jas_cmprof_t *) NULL)
601 icc_profile=jas_iccprof_createfromcmprof(cm_profile);
602 if (icc_profile != (jas_iccprof_t *) NULL)
603 {
604 jas_stream_t
605 *icc_stream;
606
607 icc_stream=jas_stream_memopen(NULL,0);
608 if ((icc_stream != (jas_stream_t *) NULL) &&
609 (jas_iccprof_save(icc_profile,icc_stream) == 0) &&
610 (jas_stream_flush(icc_stream) == 0))
611 {
612 StringInfo
613 *icc_profile,
614 *profile;
615
616 jas_stream_memobj_t
617 *blob;
618
619 /*
620 Extract the icc profile, handle errors without much noise.
621 */
622 blob=(jas_stream_memobj_t *) icc_stream->obj_;
623 if (image->debug != MagickFalse)
624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
cristye8c25f92010-06-03 00:53:06 +0000625 "Profile: ICC, %.20g bytes",(double) blob->len_);
cristy3ed852e2009-09-05 21:47:34 +0000626 profile=AcquireStringInfo(blob->len_);
627 SetStringInfoDatum(profile,blob->buf_);
628 icc_profile=(StringInfo *) GetImageProfile(image,"icc");
629 if (icc_profile == (StringInfo *) NULL)
630 (void) SetImageProfile(image,"icc",profile);
631 else
632 (void) ConcatenateStringInfo(icc_profile,profile);
633 profile=DestroyStringInfo(profile);
634 (void) jas_stream_close(icc_stream);
635 }
636 }
637 (void) jas_stream_close(jp2_stream);
638 jas_image_destroy(jp2_image);
cristybb503372010-05-27 20:51:26 +0000639 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000640 jas_matrix_destroy(pixels[i]);
641 return(GetFirstImageInList(image));
642}
643#endif
644
645/*
646%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
647% %
648% %
649% %
650% R e g i s t e r J P 2 I m a g e %
651% %
652% %
653% %
654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655%
656% RegisterJP2Image() adds attributes for the JP2 image format to the list of
657% supported formats. The attributes include the image format tag, a method
658% method to read and/or write the format, whether the format supports the
659% saving of more than one frame to the same file or blob, whether the format
660% supports native in-memory I/O, and a brief description of the format.
661%
662% The format of the RegisterJP2Image method is:
663%
cristybb503372010-05-27 20:51:26 +0000664% size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000665%
666*/
cristybb503372010-05-27 20:51:26 +0000667ModuleExport size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000668{
669 MagickInfo
670 *entry;
671
672 entry=SetMagickInfo("JP2");
673 entry->description=ConstantString("JPEG-2000 File Format Syntax");
674 entry->module=ConstantString("JP2");
675 entry->magick=(IsImageFormatHandler *) IsJP2;
676 entry->adjoin=MagickFalse;
677 entry->seekable_stream=MagickTrue;
678 entry->thread_support=NoThreadSupport;
679#if defined(MAGICKCORE_JP2_DELEGATE)
680 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
681 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
682#endif
683 (void) RegisterMagickInfo(entry);
684 entry=SetMagickInfo("JPC");
685 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000686 entry->module=ConstantString("JP2");
687 entry->magick=(IsImageFormatHandler *) IsJPC;
688 entry->adjoin=MagickFalse;
689 entry->seekable_stream=MagickTrue;
690 entry->thread_support=NoThreadSupport;
691#if defined(MAGICKCORE_JP2_DELEGATE)
692 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
693 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
694#endif
695 (void) RegisterMagickInfo(entry);
696 entry=SetMagickInfo("J2C");
697 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
698 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000699 entry->magick=(IsImageFormatHandler *) IsJPC;
700 entry->adjoin=MagickFalse;
701 entry->seekable_stream=MagickTrue;
702 entry->thread_support=NoThreadSupport;
703#if defined(MAGICKCORE_JP2_DELEGATE)
704 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
705 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
706#endif
707 (void) RegisterMagickInfo(entry);
708 entry=SetMagickInfo("JPX");
709 entry->description=ConstantString("JPEG-2000 File Format Syntax");
cristy02f1fda2009-12-10 15:23:56 +0000710 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000711 entry->magick=(IsImageFormatHandler *) IsJPC;
712 entry->adjoin=MagickFalse;
713 entry->seekable_stream=MagickTrue;
714 entry->thread_support=NoThreadSupport;
715#if defined(MAGICKCORE_JP2_DELEGATE)
716 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
717 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
718#endif
719 (void) RegisterMagickInfo(entry);
720 entry=SetMagickInfo("PGX");
721 entry->description=ConstantString("JPEG-2000 VM Format");
cristy02f1fda2009-12-10 15:23:56 +0000722 entry->module=ConstantString("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000723 entry->magick=(IsImageFormatHandler *) IsJPC;
724 entry->adjoin=MagickFalse;
725 entry->seekable_stream=MagickTrue;
726 entry->thread_support=NoThreadSupport;
727#if defined(MAGICKCORE_JP2_DELEGATE)
728 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
729#endif
730 (void) RegisterMagickInfo(entry);
731#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000732 if (instantiate_jp2 == MagickFalse)
733 {
734 jas_init();
735 instantiate_jp2=MagickTrue;
736 }
cristy3ed852e2009-09-05 21:47:34 +0000737#endif
738 return(MagickImageCoderSignature);
739}
740
741/*
742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743% %
744% %
745% %
746% U n r e g i s t e r J P 2 I m a g e %
747% %
748% %
749% %
750%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751%
752% UnregisterJP2Image() removes format registrations made by the JP2 module
753% from the list of supported formats.
754%
755% The format of the UnregisterJP2Image method is:
756%
757% UnregisterJP2Image(void)
758%
759*/
760ModuleExport void UnregisterJP2Image(void)
761{
cristy3ed852e2009-09-05 21:47:34 +0000762 (void) UnregisterMagickInfo("PGX");
cristy02f1fda2009-12-10 15:23:56 +0000763 (void) UnregisterMagickInfo("J2C");
764 (void) UnregisterMagickInfo("JPC");
765 (void) UnregisterMagickInfo("JP2");
cristy3ed852e2009-09-05 21:47:34 +0000766#if defined(MAGICKCORE_JP2_DELEGATE)
cristy1e09ca22009-12-27 18:04:58 +0000767 if (instantiate_jp2 != MagickFalse)
768 {
cristy992729d2010-01-01 15:45:06 +0000769 jas_cleanup();
cristy1e09ca22009-12-27 18:04:58 +0000770 instantiate_jp2=MagickFalse;
771 }
cristy3ed852e2009-09-05 21:47:34 +0000772#endif
773}
774
775#if defined(MAGICKCORE_JP2_DELEGATE)
776/*
777%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
778% %
779% %
780% %
781% W r i t e J P 2 I m a g e %
782% %
783% %
784% %
785%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786%
787% WriteJP2Image() writes an image in the JPEG 2000 image format.
788%
789% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
790%
791% The format of the WriteJP2Image method is:
792%
793% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
794%
795% A description of each parameter follows.
796%
797% o image_info: the image info.
798%
799% o image: The image.
800%
801*/
802static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image)
803{
804 char
805 *key,
806 magick[MaxTextExtent],
807 *options;
808
809 const char
810 *option;
811
cristy3ed852e2009-09-05 21:47:34 +0000812 jas_image_cmptparm_t
813 component_info[4];
814
815 jas_image_t
816 *jp2_image;
817
818 jas_matrix_t
819 *pixels[4];
820
821 jas_stream_t
822 *jp2_stream;
823
824 MagickBooleanType
825 status;
826
cristy18f8ee42009-09-30 01:04:36 +0000827 QuantumAny
828 range;
829
cristy3ed852e2009-09-05 21:47:34 +0000830 register const PixelPacket
831 *p;
832
cristybb503372010-05-27 20:51:26 +0000833 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000834 i,
835 x;
836
cristybb503372010-05-27 20:51:26 +0000837 size_t
cristy3ed852e2009-09-05 21:47:34 +0000838 number_components;
839
cristy524222d2011-04-25 00:37:06 +0000840 ssize_t
841 format,
842 y;
843
cristy3ed852e2009-09-05 21:47:34 +0000844 /*
845 Open image file.
846 */
847 assert(image_info != (const ImageInfo *) NULL);
848 assert(image_info->signature == MagickSignature);
849 assert(image != (Image *) NULL);
850 assert(image->signature == MagickSignature);
851 if (image->debug != MagickFalse)
852 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
853 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
854 if (status == MagickFalse)
855 return(status);
856 /*
857 Intialize JPEG 2000 API.
858 */
859 if (image->colorspace != RGBColorspace)
860 (void) TransformImageColorspace(image,RGBColorspace);
861 jp2_stream=JP2StreamManager(image);
862 if (jp2_stream == (jas_stream_t *) NULL)
863 ThrowWriterException(DelegateError,"UnableToManageJP2Stream");
864 number_components=image->matte ? 4UL : 3UL;
865 if ((image_info->type != TrueColorType) &&
cristy42a6bc82010-04-16 15:52:04 +0000866 (IsGrayImage(image,&image->exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000867 number_components=1;
868 if ((image->columns != (unsigned int) image->columns) ||
869 (image->rows != (unsigned int) image->rows))
870 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
cristy30faca22009-09-29 13:49:52 +0000871 (void) ResetMagickMemory(&component_info,0,sizeof(component_info));
cristybb503372010-05-27 20:51:26 +0000872 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000873 {
874 component_info[i].tlx=0;
875 component_info[i].tly=0;
876 component_info[i].hstep=1;
877 component_info[i].vstep=1;
878 component_info[i].width=(unsigned int) image->columns;
879 component_info[i].height=(unsigned int) image->rows;
cristy30faca22009-09-29 13:49:52 +0000880 component_info[i].prec=(int) MagickMax(MagickMin(image->depth,16),2);
cristy3ed852e2009-09-05 21:47:34 +0000881 component_info[i].sgnd=MagickFalse;
882 }
883 jp2_image=jas_image_create((int) number_components,component_info,
884 JAS_CLRSPC_UNKNOWN);
885 if (jp2_image == (jas_image_t *) NULL)
886 ThrowWriterException(DelegateError,"UnableToCreateImage");
887 if (number_components == 1)
888 {
889 /*
890 sRGB Grayscale.
891 */
892 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SGRAY);
893 jas_image_setcmpttype(jp2_image,0,
894 JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y));
895 }
896 else
897 {
898 /*
899 sRGB.
900 */
cristy3ed852e2009-09-05 21:47:34 +0000901 jas_image_setclrspc(jp2_image,JAS_CLRSPC_SRGB);
902 jas_image_setcmpttype(jp2_image,0,
cristy722af032009-12-04 01:50:06 +0000903 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
cristy3ed852e2009-09-05 21:47:34 +0000904 jas_image_setcmpttype(jp2_image,1,
cristy722af032009-12-04 01:50:06 +0000905 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
cristy3ed852e2009-09-05 21:47:34 +0000906 jas_image_setcmpttype(jp2_image,2,
cristy722af032009-12-04 01:50:06 +0000907 (jas_image_cmpttype_t) JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
cristy30faca22009-09-29 13:49:52 +0000908 if (number_components == 4)
909 jas_image_setcmpttype(jp2_image,3,JAS_IMAGE_CT_OPACITY);
cristy3ed852e2009-09-05 21:47:34 +0000910 }
911 /*
912 Convert to JPEG 2000 pixels.
913 */
cristybb503372010-05-27 20:51:26 +0000914 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000915 {
916 pixels[i]=jas_matrix_create(1,(int) image->columns);
917 if (pixels[i] == (jas_matrix_t *) NULL)
918 {
919 for (x=0; x < i; x++)
920 jas_matrix_destroy(pixels[x]);
921 jas_image_destroy(jp2_image);
922 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
923 }
924 }
cristybb503372010-05-27 20:51:26 +0000925 range=GetQuantumRange((size_t) component_info[0].prec);
926 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000927 {
928 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
929 if (p == (const PixelPacket *) NULL)
930 break;
cristybb503372010-05-27 20:51:26 +0000931 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000932 {
933 if (number_components == 1)
cristy722af032009-12-04 01:50:06 +0000934 jas_matrix_setv(pixels[0],x,(jas_seqent_t) ScaleQuantumToAny(
935 PixelIntensityToQuantum(p),range));
cristy3ed852e2009-09-05 21:47:34 +0000936 else
937 {
cristy722af032009-12-04 01:50:06 +0000938 jas_matrix_setv(pixels[0],x,(jas_seqent_t)
cristy89bbeaf2011-04-22 20:25:27 +0000939 ScaleQuantumToAny(GetRedPixelComponent(p),range));
cristy722af032009-12-04 01:50:06 +0000940 jas_matrix_setv(pixels[1],x,(jas_seqent_t)
cristy89bbeaf2011-04-22 20:25:27 +0000941 ScaleQuantumToAny(GetGreenPixelComponent(p),range));
cristy722af032009-12-04 01:50:06 +0000942 jas_matrix_setv(pixels[2],x,(jas_seqent_t)
cristy89bbeaf2011-04-22 20:25:27 +0000943 ScaleQuantumToAny(GetBluePixelComponent(p),range));
cristy3ed852e2009-09-05 21:47:34 +0000944 if (number_components > 3)
cristy722af032009-12-04 01:50:06 +0000945 jas_matrix_setv(pixels[3],x,(jas_seqent_t)
cristy46f08202010-01-10 04:04:21 +0000946 ScaleQuantumToAny((Quantum) (GetAlphaPixelComponent(p)),range));
cristy3ed852e2009-09-05 21:47:34 +0000947 }
948 p++;
949 }
cristybb503372010-05-27 20:51:26 +0000950 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000951 (void) jas_image_writecmpt(jp2_image,(short) i,0,(unsigned int) y,
952 (unsigned int) image->columns,1,pixels[i]);
cristycee97112010-05-28 00:44:52 +0000953 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000954 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000955 if (status == MagickFalse)
956 break;
957 }
958 (void) CopyMagickString(magick,image_info->magick,MaxTextExtent);
cristy02f1fda2009-12-10 15:23:56 +0000959 if (LocaleCompare(magick,"J2C") == 0)
960 (void) CopyMagickString(magick,"JPC",MaxTextExtent);
cristy3ed852e2009-09-05 21:47:34 +0000961 LocaleLower(magick);
962 format=jas_image_strtofmt(magick);
963 options=(char *) NULL;
964 ResetImageOptionIterator(image_info);
965 key=GetNextImageOption(image_info);
cristy722af032009-12-04 01:50:06 +0000966 for ( ; key != (char *) NULL; key=GetNextImageOption(image_info))
cristy3ed852e2009-09-05 21:47:34 +0000967 {
968 option=GetImageOption(image_info,key);
cristy722af032009-12-04 01:50:06 +0000969 if (option == (const char *) NULL)
970 continue;
971 if (LocaleNCompare(key,"jp2:",4) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000972 {
cristy722af032009-12-04 01:50:06 +0000973 (void) ConcatenateString(&options,key+4);
974 if (*option != '\0')
cristy3ed852e2009-09-05 21:47:34 +0000975 {
cristy722af032009-12-04 01:50:06 +0000976 (void) ConcatenateString(&options,"=");
977 (void) ConcatenateString(&options,option);
cristy3ed852e2009-09-05 21:47:34 +0000978 }
cristy722af032009-12-04 01:50:06 +0000979 (void) ConcatenateString(&options," ");
980 }
cristy3ed852e2009-09-05 21:47:34 +0000981 }
982 option=GetImageOption(image_info,"jp2:rate");
983 if ((option == (const char *) NULL) &&
984 (image_info->compression != LosslessJPEGCompression) &&
985 (image->quality != UndefinedCompressionQuality) &&
cristy30faca22009-09-29 13:49:52 +0000986 ((double) image->quality <= 99.5) &&
987 ((image->rows*image->columns) > 2500))
cristy3ed852e2009-09-05 21:47:34 +0000988 {
989 char
990 option[MaxTextExtent];
991
992 double
993 alpha,
994 header_size,
995 number_pixels,
996 rate,
997 target_size;
998
999 alpha=115.0-image->quality;
1000 rate=100.0/(alpha*alpha);
1001 header_size=550.0;
1002 header_size+=(number_components-1)*142;
1003 number_pixels=(double) image->rows*image->columns*number_components*
1004 (GetImageQuantumDepth(image,MagickTrue)/8);
1005 target_size=(number_pixels*rate)+header_size;
1006 rate=target_size/number_pixels;
cristye7f51092010-01-17 00:39:37 +00001007 (void) FormatMagickString(option,MaxTextExtent,"rate=%g",rate);
cristy3ed852e2009-09-05 21:47:34 +00001008 (void) ConcatenateString(&options,option);
1009 }
1010 status=jas_image_encode(jp2_image,jp2_stream,format,options) != 0 ?
1011 MagickTrue : MagickFalse;
1012 (void) jas_stream_close(jp2_stream);
cristybb503372010-05-27 20:51:26 +00001013 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +00001014 jas_matrix_destroy(pixels[i]);
1015 jas_image_destroy(jp2_image);
1016 if (status != MagickFalse)
1017 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1018 return(MagickTrue);
1019}
1020#endif