blob: 0a8b67ad50fa9f2b4db3d8ee2aef999a38a8b015 [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% %
cristyde984cd2013-12-01 14:49:27 +000015% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000016% Nathan Brown %
17% June 2001 %
18% %
19% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 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*/
cristy061f98d2014-01-03 20:19:30 +000042#include "MagickCore/studio.h"
43#include "MagickCore/artifact.h"
44#include "MagickCore/attribute.h"
45#include "MagickCore/blob.h"
46#include "MagickCore/blob-private.h"
47#include "MagickCore/cache.h"
48#include "MagickCore/colorspace.h"
49#include "MagickCore/colorspace-private.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/option.h"
62#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/profile.h"
cristy7ea34962014-01-04 18:03:30 +000064#include "MagickCore/property.h"
cristy061f98d2014-01-03 20:19:30 +000065#include "MagickCore/quantum-private.h"
66#include "MagickCore/static.h"
67#include "MagickCore/statistic.h"
68#include "MagickCore/string_.h"
69#include "MagickCore/string-private.h"
70#include "MagickCore/module.h"
cristy25997252014-01-02 13:28:18 +000071#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
72#include <openjpeg.h>
cristy3ed852e2009-09-05 21:47:34 +000073#endif
74
75/*
76 Forward declarations.
77*/
cristy25997252014-01-02 13:28:18 +000078#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +000079static MagickBooleanType
cristydb9258a2014-01-03 20:26:19 +000080 WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000081#endif
82
83/*
84%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85% %
86% %
87% %
cristyb5a97912014-01-02 15:52:07 +000088% I s J 2 K %
89% %
90% %
91% %
92%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93%
94% IsJ2K() returns MagickTrue if the image format type, identified by the
95% magick string, is J2K.
96%
97% The format of the IsJ2K method is:
98%
99% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
100%
101% A description of each parameter follows:
102%
103% o magick: compare image format pattern against these bytes.
104%
105% o length: Specifies the length of the magick string.
106%
107*/
108static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length)
109{
110 if (length < 4)
111 return(MagickFalse);
112 if (memcmp(magick,"\xff\x4f\xff\x51",4) == 0)
113 return(MagickTrue);
114 return(MagickFalse);
115}
116
117/*
118%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119% %
120% %
121% %
cristy3ed852e2009-09-05 21:47:34 +0000122% I s J P 2 %
123% %
124% %
125% %
126%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127%
128% IsJP2() returns MagickTrue if the image format type, identified by the
129% magick string, is JP2.
130%
131% The format of the IsJP2 method is:
132%
133% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
134%
135% A description of each parameter follows:
136%
137% o magick: compare image format pattern against these bytes.
138%
139% o length: Specifies the length of the magick string.
140%
141*/
142static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
143{
cristyb5a97912014-01-02 15:52:07 +0000144 if (length < 12)
cristy3ed852e2009-09-05 21:47:34 +0000145 return(MagickFalse);
cristyb5a97912014-01-02 15:52:07 +0000146 if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0)
147 return(MagickTrue);
148 if (memcmp(magick,"\x0d\x0a\x87\x0a",12) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000149 return(MagickTrue);
150 return(MagickFalse);
151}
152
153/*
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155% %
156% %
157% %
158% I s J P C %
159% %
160% %
161% %
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163%
164% IsJPC()() returns MagickTrue if the image format type, identified by the
165% magick string, is JPC.
166%
167% The format of the IsJPC method is:
168%
169% MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
170%
171% A description of each parameter follows:
172%
173% o magick: compare image format pattern against these bytes.
174%
175% o length: Specifies the length of the magick string.
176%
177*/
178static MagickBooleanType IsJPC(const unsigned char *magick,const size_t length)
179{
cristyb5a97912014-01-02 15:52:07 +0000180 if (length < 12)
cristy3ed852e2009-09-05 21:47:34 +0000181 return(MagickFalse);
cristyb5a97912014-01-02 15:52:07 +0000182 if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0)
183 return(MagickTrue);
184 if (memcmp(magick,"\x0d\x0a\x87\x0a",12) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000185 return(MagickTrue);
186 return(MagickFalse);
187}
188
189/*
190%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191% %
192% %
193% %
194% R e a d J P 2 I m a g e %
195% %
196% %
197% %
198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199%
200% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
201% codestream (JPC) image file and returns it. It allocates the memory
202% necessary for the new Image structure and returns a pointer to the new
203% image or set of images.
204%
205% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
206%
207% The format of the ReadJP2Image method is:
208%
209% Image *ReadJP2Image(const ImageInfo *image_info,
210% ExceptionInfo *exception)
211%
212% A description of each parameter follows:
213%
214% o image_info: the image info.
215%
216% o exception: return any errors or warnings in this structure.
217%
218*/
cristy25997252014-01-02 13:28:18 +0000219#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
220static void JP2ErrorHandler(const char *message,void *client_data)
cristy3ed852e2009-09-05 21:47:34 +0000221{
cristy25997252014-01-02 13:28:18 +0000222 ExceptionInfo
223 *exception;
cristy3ed852e2009-09-05 21:47:34 +0000224
cristy25997252014-01-02 13:28:18 +0000225 exception=(ExceptionInfo *) client_data;
226 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
227 message,"`%s'","OpenJP2");
228}
229
230static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context)
231{
cristy3ed852e2009-09-05 21:47:34 +0000232 Image
233 *image;
cristy3ed852e2009-09-05 21:47:34 +0000234
cristy3ed852e2009-09-05 21:47:34 +0000235 ssize_t
236 count;
237
cristy25997252014-01-02 13:28:18 +0000238 image=(Image *) context;
239 count=ReadBlob(image,(ssize_t) length,(unsigned char *) buffer);
240 if (count == 0)
241 return(-1);
242 return((OPJ_SIZE_T) count);
cristy3ed852e2009-09-05 21:47:34 +0000243}
244
cristy25997252014-01-02 13:28:18 +0000245static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context)
cristy3ed852e2009-09-05 21:47:34 +0000246{
cristy25997252014-01-02 13:28:18 +0000247 Image
248 *image;
249
250 image=(Image *) context;
cristyccfccd22014-01-03 01:17:13 +0000251 return(SeekBlob(image,offset,SEEK_SET) < 0 ? 0 : 1);
cristy25997252014-01-02 13:28:18 +0000252}
253
cristydb9258a2014-01-03 20:26:19 +0000254static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context)
cristy25997252014-01-02 13:28:18 +0000255{
256 Image
257 *image;
258
259 image=(Image *) context;
cristy4d419932014-01-03 19:18:49 +0000260 return(SeekBlob(image,offset,SEEK_CUR) < 0 ? 0 : offset);
cristy25997252014-01-02 13:28:18 +0000261}
262
263static void JP2WarningHandler(const char *message,void *client_data)
264{
265 ExceptionInfo
266 *exception;
267
268 exception=(ExceptionInfo *) client_data;
269 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
270 message,"`%s'","OpenJP2");
271}
272
273static OPJ_SIZE_T JP2WriteHandler(void *buffer,OPJ_SIZE_T length,void *context)
274{
275 Image
276 *image;
277
cristy3ed852e2009-09-05 21:47:34 +0000278 ssize_t
279 count;
280
cristy25997252014-01-02 13:28:18 +0000281 image=(Image *) context;
282 count=WriteBlob(image,(ssize_t) length,(unsigned char *) buffer);
283 return((OPJ_SIZE_T) count);
cristy3ed852e2009-09-05 21:47:34 +0000284}
285
286static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
287{
cristy77559f42014-01-02 18:54:04 +0000288 const char
289 *option;
290
cristy3ed852e2009-09-05 21:47:34 +0000291 Image
292 *image;
293
cristy25997252014-01-02 13:28:18 +0000294 int
cristy25997252014-01-02 13:28:18 +0000295 jp2_status;
cristy3ed852e2009-09-05 21:47:34 +0000296
297 MagickBooleanType
298 status;
299
cristy25997252014-01-02 13:28:18 +0000300 opj_codec_t
301 *jp2_codec;
cristy3ed852e2009-09-05 21:47:34 +0000302
cristy6c0f1502014-01-02 13:53:25 +0000303 opj_codestream_index_t
304 *codestream_index = (opj_codestream_index_t *) NULL;
305
cristy25997252014-01-02 13:28:18 +0000306 opj_dparameters_t
307 parameters;
308
309 opj_image_t
310 *jp2_image;
311
312 opj_stream_t
313 *jp2_stream;
cristy48ac1c32012-10-19 23:55:43 +0000314
cristybb503372010-05-27 20:51:26 +0000315 register ssize_t
cristy25997252014-01-02 13:28:18 +0000316 i;
cristy3ed852e2009-09-05 21:47:34 +0000317
cristy524222d2011-04-25 00:37:06 +0000318 ssize_t
cristy524222d2011-04-25 00:37:06 +0000319 y;
320
cristy3ed852e2009-09-05 21:47:34 +0000321 /*
322 Open image file.
323 */
324 assert(image_info != (const ImageInfo *) NULL);
325 assert(image_info->signature == MagickSignature);
326 if (image_info->debug != MagickFalse)
327 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
328 image_info->filename);
329 assert(exception != (ExceptionInfo *) NULL);
330 assert(exception->signature == MagickSignature);
cristydb9258a2014-01-03 20:26:19 +0000331 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000332 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
333 if (status == MagickFalse)
334 {
335 image=DestroyImageList(image);
336 return((Image *) NULL);
337 }
338 /*
cristy25997252014-01-02 13:28:18 +0000339 Initialize JP2 codec.
cristy3ed852e2009-09-05 21:47:34 +0000340 */
cristy25997252014-01-02 13:28:18 +0000341 if (LocaleCompare(image_info->magick,"JPT") == 0)
342 jp2_codec=opj_create_decompress(OPJ_CODEC_JPT);
343 else
344 if (LocaleCompare(image_info->magick,"J2K") == 0)
345 jp2_codec=opj_create_decompress(OPJ_CODEC_J2K);
346 else
347 jp2_codec=opj_create_decompress(OPJ_CODEC_JP2);
348 opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception);
349 opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception);
350 opj_set_default_decoder_parameters(&parameters);
cristya13e3672014-01-02 15:24:44 +0000351 option=GetImageOption(image_info,"jp2:reduce-factor");
352 if (option != (const char *) NULL)
353 parameters.cp_reduce=StringToInteger(option);
cristy7ea34962014-01-04 18:03:30 +0000354 option=GetImageOption(image_info,"jp2:quality-layers");
cristya13e3672014-01-02 15:24:44 +0000355 if (option != (const char *) NULL)
356 parameters.cp_layer=StringToInteger(option);
cristy25997252014-01-02 13:28:18 +0000357 if (opj_setup_decoder(jp2_codec,&parameters) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000358 {
cristy25997252014-01-02 13:28:18 +0000359 opj_destroy_codec(jp2_codec);
360 ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
361 }
cristy66d40562014-01-03 01:27:44 +0000362 jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1);
cristy25997252014-01-02 13:28:18 +0000363 opj_stream_set_read_function(jp2_stream,JP2ReadHandler);
364 opj_stream_set_write_function(jp2_stream,JP2WriteHandler);
365 opj_stream_set_seek_function(jp2_stream,JP2SeekHandler);
366 opj_stream_set_skip_function(jp2_stream,JP2SkipHandler);
367 opj_stream_set_user_data(jp2_stream,image);
368 opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image));
369 if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0)
370 {
371 opj_stream_set_user_data(jp2_stream,NULL);
372 opj_stream_destroy_v3(jp2_stream);
373 opj_destroy_codec(jp2_codec);
cristy3ed852e2009-09-05 21:47:34 +0000374 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
375 }
cristya8997c22014-01-03 00:55:02 +0000376 if ((image->columns != 0) && (image->rows != 0))
cristy25997252014-01-02 13:28:18 +0000377 {
cristy56e4faf2014-01-02 17:59:34 +0000378 /*
379 Extract an area from the image.
380 */
cristya8997c22014-01-03 00:55:02 +0000381 jp2_status=opj_set_decode_area(jp2_codec,jp2_image,image->extract_info.x,
cristy707e04c2014-01-03 01:01:18 +0000382 image->extract_info.y,image->extract_info.x+image->columns,
383 image->extract_info.y+image->rows);
cristy56e4faf2014-01-02 17:59:34 +0000384 if (jp2_status == 0)
385 {
386 opj_stream_set_user_data(jp2_stream,NULL);
387 opj_stream_destroy_v3(jp2_stream);
388 opj_destroy_codec(jp2_codec);
389 opj_image_destroy(jp2_image);
390 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
391 }
cristy25997252014-01-02 13:28:18 +0000392 }
cristy7ea34962014-01-04 18:03:30 +0000393 if ((opj_decode(jp2_codec,jp2_stream,jp2_image) == 0) ||
394 (opj_end_decompress(jp2_codec,jp2_stream) == 0))
cristy25997252014-01-02 13:28:18 +0000395 {
396 opj_stream_set_user_data(jp2_stream,NULL);
397 opj_stream_destroy_v3(jp2_stream);
398 opj_destroy_codec(jp2_codec);
399 opj_image_destroy(jp2_image);
400 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
401 }
402 opj_stream_set_user_data(jp2_stream,NULL);
403 opj_stream_destroy_v3(jp2_stream);
404 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
cristy3ed852e2009-09-05 21:47:34 +0000405 {
cristy25997252014-01-02 13:28:18 +0000406 if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0))
cristy3ed852e2009-09-05 21:47:34 +0000407 {
cristy25997252014-01-02 13:28:18 +0000408 opj_stream_set_user_data(jp2_stream,NULL);
409 opj_destroy_codec(jp2_codec);
410 opj_image_destroy(jp2_image);
411 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported")
cristy3ed852e2009-09-05 21:47:34 +0000412 }
cristy3ed852e2009-09-05 21:47:34 +0000413 }
414 /*
cristy25997252014-01-02 13:28:18 +0000415 Convert JP2 image.
cristy3ed852e2009-09-05 21:47:34 +0000416 */
cristy25997252014-01-02 13:28:18 +0000417 image->columns=(size_t) jp2_image->comps[0].w;
418 image->rows=(size_t) jp2_image->comps[0].h;
cristyb3445a02014-01-04 01:00:14 +0000419 image->depth=jp2_image->comps[0].prec;
cristy25997252014-01-02 13:28:18 +0000420 image->compression=JPEG2000Compression;
421 if (jp2_image->numcomps <= 2)
cristy30faca22009-09-29 13:49:52 +0000422 {
cristydb9258a2014-01-03 20:26:19 +0000423 SetImageColorspace(image,GRAYColorspace,exception);
cristy25997252014-01-02 13:28:18 +0000424 if (jp2_image->numcomps > 1)
cristydb9258a2014-01-03 20:26:19 +0000425 image->alpha_trait=BlendPixelTrait;
cristy30faca22009-09-29 13:49:52 +0000426 }
cristy25997252014-01-02 13:28:18 +0000427 if (jp2_image->numcomps > 3)
cristydb9258a2014-01-03 20:26:19 +0000428 image->alpha_trait=BlendPixelTrait;
cristy25997252014-01-02 13:28:18 +0000429 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
430 {
431 if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0))
432 {
433 opj_stream_set_user_data(jp2_stream,NULL);
434 opj_destroy_codec(jp2_codec);
435 opj_image_destroy(jp2_image);
436 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported")
437 }
438 if ((jp2_image->comps[i].dx > 1) || (jp2_image->comps[i].dy > 1))
439 image->colorspace=YUVColorspace;
440 }
cristye173c292014-01-02 13:43:12 +0000441 if (jp2_image->icc_profile_buf != (unsigned char *) NULL)
442 {
443 StringInfo
444 *profile;
445
446 profile=BlobToStringInfo(jp2_image->icc_profile_buf,
447 jp2_image->icc_profile_len);
448 if (profile != (StringInfo *) NULL)
cristydb9258a2014-01-03 20:26:19 +0000449 SetImageProfile(image,"icc",profile,exception);
cristye173c292014-01-02 13:43:12 +0000450 }
cristybb503372010-05-27 20:51:26 +0000451 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000452 {
cristydb9258a2014-01-03 20:26:19 +0000453 register Quantum
cristy25997252014-01-02 13:28:18 +0000454 *restrict q;
455
456 register ssize_t
457 x;
458
cristy3ed852e2009-09-05 21:47:34 +0000459 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristydb9258a2014-01-03 20:26:19 +0000460 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000461 break;
cristy25997252014-01-02 13:28:18 +0000462 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000463 {
cristy25997252014-01-02 13:28:18 +0000464 register ssize_t
465 i;
466
467 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
cristy3ed852e2009-09-05 21:47:34 +0000468 {
cristy25997252014-01-02 13:28:18 +0000469 double
470 pixel,
471 scale;
472
473 scale=QuantumRange/(double) ((1UL << jp2_image->comps[i].prec)-1);
474 pixel=scale*(jp2_image->comps[i].data[y/jp2_image->comps[i].dy*
475 image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx]+
476 (jp2_image->comps[i].sgnd ? 1UL << (jp2_image->comps[i].prec-1) : 0));
477 switch (i)
cristy3ed852e2009-09-05 21:47:34 +0000478 {
cristy25997252014-01-02 13:28:18 +0000479 case 0:
480 {
481 SetPixelRed(image,ClampToQuantum(pixel),q);
482 SetPixelGreen(image,ClampToQuantum(pixel),q);
483 SetPixelBlue(image,ClampToQuantum(pixel),q);
484 SetPixelAlpha(image,OpaqueAlpha,q);
485 break;
486 }
487 case 1:
488 {
489 if (jp2_image->numcomps == 2)
490 {
491 SetPixelAlpha(image,ClampToQuantum(pixel),q);
492 break;
493 }
494 SetPixelGreen(image,ClampToQuantum(pixel),q);
495 break;
496 }
497 case 2:
498 {
499 SetPixelBlue(image,ClampToQuantum(pixel),q);
500 break;
501 }
502 case 3:
503 {
504 SetPixelAlpha(image,ClampToQuantum(pixel),q);
505 break;
506 }
cristy3ed852e2009-09-05 21:47:34 +0000507 }
cristy3ed852e2009-09-05 21:47:34 +0000508 }
cristy25997252014-01-02 13:28:18 +0000509 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000510 }
511 if (SyncAuthenticPixels(image,exception) == MagickFalse)
512 break;
cristycee97112010-05-28 00:44:52 +0000513 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000514 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000515 if (status == MagickFalse)
516 break;
517 }
cristy25997252014-01-02 13:28:18 +0000518 /*
519 Free resources.
520 */
521 opj_destroy_codec(jp2_codec);
522 opj_image_destroy(jp2_image);
cristy6c0f1502014-01-02 13:53:25 +0000523 opj_destroy_cstr_index(&codestream_index);
cristy3ed852e2009-09-05 21:47:34 +0000524 return(GetFirstImageInList(image));
525}
526#endif
527
528/*
529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530% %
531% %
532% %
533% R e g i s t e r J P 2 I m a g e %
534% %
535% %
536% %
537%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
538%
539% RegisterJP2Image() adds attributes for the JP2 image format to the list of
540% supported formats. The attributes include the image format tag, a method
541% method to read and/or write the format, whether the format supports the
542% saving of more than one frame to the same file or blob, whether the format
543% supports native in-memory I/O, and a brief description of the format.
544%
545% The format of the RegisterJP2Image method is:
546%
cristybb503372010-05-27 20:51:26 +0000547% size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000548%
549*/
cristybb503372010-05-27 20:51:26 +0000550ModuleExport size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000551{
cristybcd740c2014-01-03 21:08:39 +0000552 char
553 version[MaxTextExtent];
554
cristy3ed852e2009-09-05 21:47:34 +0000555 MagickInfo
556 *entry;
557
cristybcd740c2014-01-03 21:08:39 +0000558 *version='\0';
559#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
560 (void) FormatLocaleString(version,MaxTextExtent,"%s",opj_version());
561#endif
cristy3ed852e2009-09-05 21:47:34 +0000562 entry=SetMagickInfo("JP2");
563 entry->description=ConstantString("JPEG-2000 File Format Syntax");
cristybcd740c2014-01-03 21:08:39 +0000564 if (*version != '\0')
565 entry->version=ConstantString(version);
cristy5aefbeb2013-08-09 12:13:32 +0000566 entry->mime_type=ConstantString("image/jp2");
cristy3ed852e2009-09-05 21:47:34 +0000567 entry->module=ConstantString("JP2");
568 entry->magick=(IsImageFormatHandler *) IsJP2;
569 entry->adjoin=MagickFalse;
570 entry->seekable_stream=MagickTrue;
571 entry->thread_support=NoThreadSupport;
cristy25997252014-01-02 13:28:18 +0000572#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000573 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
574 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
575#endif
576 (void) RegisterMagickInfo(entry);
cristy9bfeb942012-05-10 12:07:32 +0000577 entry=SetMagickInfo("J2K");
578 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
cristybcd740c2014-01-03 21:08:39 +0000579 if (*version != '\0')
580 entry->version=ConstantString(version);
cristy5aefbeb2013-08-09 12:13:32 +0000581 entry->mime_type=ConstantString("image/jp2");
cristy9bfeb942012-05-10 12:07:32 +0000582 entry->module=ConstantString("JP2");
cristyb5a97912014-01-02 15:52:07 +0000583 entry->magick=(IsImageFormatHandler *) IsJ2K;
cristy9bfeb942012-05-10 12:07:32 +0000584 entry->adjoin=MagickFalse;
585 entry->seekable_stream=MagickTrue;
586 entry->thread_support=NoThreadSupport;
cristy25997252014-01-02 13:28:18 +0000587#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy9bfeb942012-05-10 12:07:32 +0000588 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
589 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
590#endif
591 (void) RegisterMagickInfo(entry);
cristy25997252014-01-02 13:28:18 +0000592 entry=SetMagickInfo("JPT");
cristy3ed852e2009-09-05 21:47:34 +0000593 entry->description=ConstantString("JPEG-2000 File Format Syntax");
cristybcd740c2014-01-03 21:08:39 +0000594 if (*version != '\0')
595 entry->version=ConstantString(version);
cristy5aefbeb2013-08-09 12:13:32 +0000596 entry->mime_type=ConstantString("image/jp2");
cristy02f1fda2009-12-10 15:23:56 +0000597 entry->module=ConstantString("JP2");
cristy25997252014-01-02 13:28:18 +0000598 entry->magick=(IsImageFormatHandler *) IsJP2;
cristy3ed852e2009-09-05 21:47:34 +0000599 entry->adjoin=MagickFalse;
600 entry->seekable_stream=MagickTrue;
601 entry->thread_support=NoThreadSupport;
cristy25997252014-01-02 13:28:18 +0000602#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000603 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
604 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
605#endif
606 (void) RegisterMagickInfo(entry);
cristy92f84a52014-01-02 15:10:12 +0000607 entry=SetMagickInfo("JPC");
608 entry->description=ConstantString("JPEG-2000 Code Stream Syntax");
cristybcd740c2014-01-03 21:08:39 +0000609 if (*version != '\0')
610 entry->version=ConstantString(version);
cristy92f84a52014-01-02 15:10:12 +0000611 entry->mime_type=ConstantString("image/jp2");
612 entry->module=ConstantString("JP2");
613 entry->magick=(IsImageFormatHandler *) IsJPC;
614 entry->adjoin=MagickFalse;
615 entry->seekable_stream=MagickTrue;
616 entry->thread_support=NoThreadSupport;
617#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
618 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
619 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
620#endif
621 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +0000622 return(MagickImageCoderSignature);
623}
624
625/*
626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
627% %
628% %
629% %
630% U n r e g i s t e r J P 2 I m a g e %
631% %
632% %
633% %
634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
635%
636% UnregisterJP2Image() removes format registrations made by the JP2 module
637% from the list of supported formats.
638%
639% The format of the UnregisterJP2Image method is:
640%
641% UnregisterJP2Image(void)
642%
643*/
644ModuleExport void UnregisterJP2Image(void)
645{
cristy92f84a52014-01-02 15:10:12 +0000646 (void) UnregisterMagickInfo("JPC");
cristy25997252014-01-02 13:28:18 +0000647 (void) UnregisterMagickInfo("JPT");
cristy02f1fda2009-12-10 15:23:56 +0000648 (void) UnregisterMagickInfo("JP2");
cristy25997252014-01-02 13:28:18 +0000649 (void) UnregisterMagickInfo("J2K");
cristy3ed852e2009-09-05 21:47:34 +0000650}
651
cristy25997252014-01-02 13:28:18 +0000652#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000653/*
654%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
655% %
656% %
657% %
658% W r i t e J P 2 I m a g e %
659% %
660% %
661% %
662%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
663%
664% WriteJP2Image() writes an image in the JPEG 2000 image format.
665%
666% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
667%
668% The format of the WriteJP2Image method is:
669%
cristydb9258a2014-01-03 20:26:19 +0000670% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
671% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000672%
673% A description of each parameter follows.
674%
675% o image_info: the image info.
676%
677% o image: The image.
678%
679*/
cristydb9258a2014-01-03 20:26:19 +0000680static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
681 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000682{
cristy7ea34962014-01-04 18:03:30 +0000683 const char
cristy73bbee12014-01-05 00:43:38 +0000684 *option,
685 *property;
cristy7ea34962014-01-04 18:03:30 +0000686
687 int
688 jp2_status;
689
cristy3ed852e2009-09-05 21:47:34 +0000690 MagickBooleanType
691 status;
692
cristy7ea34962014-01-04 18:03:30 +0000693 opj_codec_t
694 *jp2_codec;
695
696 OPJ_COLOR_SPACE
697 jp2_colorspace;
698
699 opj_cparameters_t
700 parameters;
701
702 opj_image_cmptparm_t
703 jp2_info[5];
704
705 opj_image_t
706 *jp2_image;
707
708 opj_stream_t
709 *jp2_stream;
710
711 register ssize_t
712 i;
713
714 ssize_t
715 y;
716
717 size_t
718 channels;
719
cristy3ed852e2009-09-05 21:47:34 +0000720 /*
721 Open image file.
722 */
723 assert(image_info != (const ImageInfo *) NULL);
724 assert(image_info->signature == MagickSignature);
725 assert(image != (Image *) NULL);
726 assert(image->signature == MagickSignature);
727 if (image->debug != MagickFalse)
728 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7ea34962014-01-04 18:03:30 +0000729 assert(exception != (ExceptionInfo *) NULL);
730 assert(exception->signature == MagickSignature);
cristydb9258a2014-01-03 20:26:19 +0000731 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000732 if (status == MagickFalse)
733 return(status);
734 /*
glennrpbca49a22011-07-01 12:18:22 +0000735 Initialize JPEG 2000 API.
cristy3ed852e2009-09-05 21:47:34 +0000736 */
cristy7ea34962014-01-04 18:03:30 +0000737 opj_set_default_encoder_parameters(&parameters);
738 for (i=1; i < 6; i++)
739 if (((1UL << (i+2)) > image->columns) && ((1UL << (i+2)) > image->rows))
740 break;
741 parameters.numresolution=i;
cristy73bbee12014-01-05 00:43:38 +0000742 option=GetImageOption(image_info,"jp2:number-resolutions");
743 if (option != (const char *) NULL)
744 parameters.numresolution=StringToInteger(option);
cristy7ea34962014-01-04 18:03:30 +0000745 parameters.tcp_numlayers=1;
746 parameters.tcp_distoratio[0]=(double) image->quality;
747 parameters.cp_fixed_quality=OPJ_TRUE;
748 if (image_info->extract != (char *) NULL)
749 {
750 RectangleInfo
751 geometry;
752
753 int
754 flags;
755
756 /*
757 Set tile size.
758 */
759 flags=ParseAbsoluteGeometry(image_info->extract,&geometry);
760 parameters.cp_tdx=geometry.width;
761 parameters.cp_tdy=geometry.width;
762 if ((flags & HeightValue) != 0)
763 parameters.cp_tdy=geometry.height;
764 if ((flags & XValue) != 0)
765 parameters.cp_tx0=geometry.x;
766 if ((flags & YValue) != 0)
767 parameters.cp_ty0=geometry.y;
768 parameters.tile_size_on=OPJ_TRUE;
769 }
cristy73bbee12014-01-05 00:43:38 +0000770 option=GetImageOption(image_info,"jp2:quality");
771 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000772 {
773 register const char
774 *p;
775
776 /*
777 Set quality PSNR.
778 */
cristy73bbee12014-01-05 00:43:38 +0000779 p=option;
cristy7ea34962014-01-04 18:03:30 +0000780 for (i=1; sscanf(p,"%f",&parameters.tcp_distoratio[i]) == 1; i++)
781 {
782 if (i > 100)
783 break;
784 while ((*p != '\0') && (*p != ','))
785 p++;
786 if (*p == '\0')
787 break;
788 p++;
789 }
790 parameters.tcp_numlayers=i;
791 parameters.cp_fixed_quality=OPJ_TRUE;
792 }
cristy73bbee12014-01-05 00:43:38 +0000793 option=GetImageOption(image_info,"jp2:rate");
794 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000795 {
796 register const char
797 *p;
798
799 /*
800 Set compression rate.
801 */
cristy73bbee12014-01-05 00:43:38 +0000802 p=option;
cristy7ea34962014-01-04 18:03:30 +0000803 for (i=1; sscanf(p,"%f",&parameters.tcp_rates[i]) == 1; i++)
804 {
805 if (i > 100)
806 break;
807 while ((*p != '\0') && (*p != ','))
808 p++;
809 if (*p == '\0')
810 break;
811 p++;
812 }
813 parameters.tcp_numlayers=i;
814 parameters.cp_disto_alloc=OPJ_TRUE;
815 }
cristy73bbee12014-01-05 00:43:38 +0000816 option=GetImageOption(image_info,"jp2:sampling-factor");
817 if (option != (const char *) NULL)
818 (void) sscanf(option,"%d,%d",&parameters.subsampling_dx,
819 &parameters.subsampling_dy);
cristy1803a902014-01-04 21:01:24 +0000820#if defined(BUGINOPENJPEG)
821 if ((image->depth == 12) &&
822 ((image->columns == 2048) || (image->rows == 1080) ||
823 (image->columns == 4096) || (image->rows == 2160)))
824 {
825 /*
826 Digital Cinema profile compliance.
827 */
828 if ((image->columns == 2048) || (image->rows == 1080))
829 {
830 /*
831 Digital Cinema 2K.
832 */
833 parameters.cp_cinema=OPJ_CINEMA2K_48;
834 parameters.cp_rsiz=OPJ_CINEMA2K;
835 }
836 if ((image->columns == 4096) || (image->rows == 2160))
837 {
838 /*
839 Digital Cinema 4K.
840 */
841 parameters.cp_cinema=OPJ_CINEMA4K_24;
842 parameters.cp_rsiz=OPJ_CINEMA4K;
843 }
844 }
845#endif
cristy73bbee12014-01-05 00:43:38 +0000846 property=GetImageProperty(image,"comment",exception);
847 if (property != (const char *) NULL)
848 parameters.cp_comment=ConstantString(property);
cristy7ea34962014-01-04 18:03:30 +0000849 channels=3;
850 jp2_colorspace=OPJ_CLRSPC_SRGB;
851 if (image->colorspace == YUVColorspace)
852 {
853 jp2_colorspace=OPJ_CLRSPC_SYCC;
854 parameters.subsampling_dx=2;
855 }
856 else
857 {
858 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
859 (void) TransformImageColorspace(image,sRGBColorspace,exception);
860 if (IsGrayColorspace(image->colorspace) != MagickFalse)
861 {
862 channels=1;
863 jp2_colorspace=OPJ_CLRSPC_GRAY;
864 }
865 if (image->alpha_trait == BlendPixelTrait)
866 channels++;
867 }
868 ResetMagickMemory(jp2_info,0,sizeof(jp2_info));
869 for (i=0; i < (ssize_t) channels; i++)
870 {
871 jp2_info[i].prec=image->depth;
872 jp2_info[i].bpp=image->depth;
873 if ((image->depth == 1) &&
874 ((LocaleCompare(image_info->magick,"JPT") == 0) ||
875 (LocaleCompare(image_info->magick,"JP2") == 0)))
876 {
877 jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */
878 jp2_info[i].bpp++;
879 }
880 jp2_info[i].sgnd=0;
881 jp2_info[i].dx=parameters.subsampling_dx;
882 jp2_info[i].dy=parameters.subsampling_dy;
883 jp2_info[i].w=image->columns;
884 jp2_info[i].h=image->rows;
885 }
886 jp2_image=opj_image_create(channels,jp2_info,jp2_colorspace);
887 if (jp2_image == (opj_image_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000888 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
cristy7ea34962014-01-04 18:03:30 +0000889 jp2_image->x0=parameters.image_offset_x0;
890 jp2_image->y0=parameters.image_offset_y0;
891 jp2_image->x1=2*parameters.image_offset_x0+(image->columns-1)*
892 parameters.subsampling_dx+1;
893 jp2_image->y1=2*parameters.image_offset_y0+(image->rows-1)*
894 parameters.subsampling_dx+1;
895 /*
896 Convert to JP2 pixels.
897 */
898 for (y=0; y < (ssize_t) image->rows; y++)
899 {
900 register const Quantum
901 *p;
902
903 ssize_t
904 x;
905
906 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
907 if (p == (const Quantum *) NULL)
908 break;
909 for (x=0; x < (ssize_t) image->columns; x++)
910 {
911 for (i=0; i < (ssize_t) channels; i++)
912 {
913 double
914 scale;
915
916 register int
917 *q;
918
919 scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange;
920 q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy*
921 image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx);
922 switch (i)
923 {
924 case 0:
925 {
926 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
927 {
928 *q=(int) (scale*GetPixelLuma(image,p));
929 break;
930 }
931 *q=(int) (scale*GetPixelRed(image,p));
932 break;
933 }
934 case 1:
935 {
936 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
937 {
938 *q=(int) (scale*GetPixelAlpha(image,p));
939 break;
940 }
941 *q=(int) (scale*GetPixelGreen(image,p));
942 break;
943 }
944 case 2:
945 {
946 *q=(int) (scale*GetPixelBlue(image,p));
947 break;
948 }
949 case 3:
950 {
951 *q=(int) (scale*GetPixelAlpha(image,p));
952 break;
953 }
954 }
955 }
956 p++;
957 }
958 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
959 image->rows);
960 if (status == MagickFalse)
961 break;
962 }
963 if (LocaleCompare(image_info->magick,"JPT") == 0)
964 jp2_codec=opj_create_compress(OPJ_CODEC_JPT);
965 else
966 if (LocaleCompare(image_info->magick,"J2K") == 0)
967 jp2_codec=opj_create_compress(OPJ_CODEC_J2K);
968 else
969 jp2_codec=opj_create_compress(OPJ_CODEC_JP2);
970 opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception);
971 opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception);
972 opj_setup_encoder(jp2_codec,&parameters,jp2_image);
973 jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE);
974 opj_stream_set_read_function(jp2_stream,JP2ReadHandler);
975 opj_stream_set_write_function(jp2_stream,JP2WriteHandler);
976 opj_stream_set_seek_function(jp2_stream,JP2SeekHandler);
977 opj_stream_set_skip_function(jp2_stream,JP2SkipHandler);
978 opj_stream_set_user_data(jp2_stream,image);
979 if (jp2_stream == (opj_stream_t *) NULL)
980 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
981 jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream);
982 if (jp2_status == 0)
983 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
984 if ((opj_encode(jp2_codec,jp2_stream) == 0) ||
985 (opj_end_compress(jp2_codec,jp2_stream) == 0))
986 {
987 opj_stream_set_user_data(jp2_stream,NULL);
988 opj_stream_destroy_v3(jp2_stream);
989 opj_destroy_codec(jp2_codec);
990 opj_image_destroy(jp2_image);
991 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
992 }
993 /*
994 Free resources.
995 */
996 opj_stream_set_user_data(jp2_stream,NULL);
997 opj_stream_destroy_v3(jp2_stream);
998 opj_destroy_codec(jp2_codec);
999 opj_image_destroy(jp2_image);
1000 (void) CloseBlob(image);
cristy3ed852e2009-09-05 21:47:34 +00001001 return(MagickTrue);
1002}
1003#endif