blob: 56cf3d04852d0a2bf08cf141f18ebe632e37b851 [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*/
cristy6d614502014-01-05 14:27:40 +0000680
681static void CinemaProfileCompliance(const opj_image_t *jp2_image,
682 opj_cparameters_t *parameters)
683{
684 /*
cristy121d6602014-01-05 14:35:51 +0000685 Digital Cinema 4K profile compliant codestream.
cristy6d614502014-01-05 14:27:40 +0000686 */
687 parameters->tile_size_on=OPJ_FALSE;
688 parameters->cp_tdx=1;
689 parameters->cp_tdy=1;
690 parameters->tp_flag='C';
691 parameters->tp_on=1;
692 parameters->cp_tx0=0;
693 parameters->cp_ty0=0;
694 parameters->image_offset_x0=0;
695 parameters->image_offset_y0=0;
696 parameters->cblockw_init=32;
697 parameters->cblockh_init=32;
698 parameters->csty|=0x01;
699 parameters->prog_order=OPJ_CPRL;
700 parameters->roi_compno=(-1);
701 parameters->subsampling_dx=1;
702 parameters->subsampling_dy=1;
703 parameters->irreversible=1;
704 if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080))
705 {
706 /*
707 Digital Cinema 2K.
708 */
709 parameters->cp_cinema=OPJ_CINEMA2K_24;
710 parameters->cp_rsiz=OPJ_CINEMA2K;
711 parameters->max_comp_size=1041666;
712 if (parameters->numresolution > 6)
713 parameters->numresolution=6;
714
715 }
716 if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160))
717 {
718 /*
719 Digital Cinema 4K.
720 */
721 parameters->cp_cinema=OPJ_CINEMA4K_24;
722 parameters->cp_rsiz=OPJ_CINEMA4K;
723 parameters->max_comp_size=1041666;
724 if (parameters->numresolution < 1)
725 parameters->numresolution=1;
726 if (parameters->numresolution > 7)
727 parameters->numresolution=7;
728 parameters->numpocs=2;
729 parameters->POC[0].tile=1;
730 parameters->POC[0].resno0=0;
731 parameters->POC[0].compno0=0;
732 parameters->POC[0].layno1=1;
733 parameters->POC[0].resno1=parameters->numresolution-1;
734 parameters->POC[0].compno1=3;
735 parameters->POC[0].prg1=OPJ_CPRL;
736 parameters->POC[1].tile=1;
737 parameters->POC[1].resno0=parameters->numresolution-1;
738 parameters->POC[1].compno0=0;
739 parameters->POC[1].layno1=1;
740 parameters->POC[1].resno1=parameters->numresolution;
741 parameters->POC[1].compno1=3;
742 parameters->POC[1].prg1=OPJ_CPRL;
743 }
744 parameters->tcp_numlayers=1;
745 parameters->tcp_rates[0]=((float) (jp2_image->numcomps*jp2_image->comps[0].w*
746 jp2_image->comps[0].h*jp2_image->comps[0].prec))/(parameters->max_comp_size*
747 8*jp2_image->comps[0].dx*jp2_image->comps[0].dy);
748 parameters->cp_disto_alloc=1;
749}
750
cristydb9258a2014-01-03 20:26:19 +0000751static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
752 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000753{
cristy7ea34962014-01-04 18:03:30 +0000754 const char
cristy73bbee12014-01-05 00:43:38 +0000755 *option,
756 *property;
cristy7ea34962014-01-04 18:03:30 +0000757
758 int
759 jp2_status;
760
cristy3ed852e2009-09-05 21:47:34 +0000761 MagickBooleanType
762 status;
763
cristy7ea34962014-01-04 18:03:30 +0000764 opj_codec_t
765 *jp2_codec;
766
767 OPJ_COLOR_SPACE
768 jp2_colorspace;
769
770 opj_cparameters_t
771 parameters;
772
773 opj_image_cmptparm_t
774 jp2_info[5];
775
776 opj_image_t
777 *jp2_image;
778
779 opj_stream_t
780 *jp2_stream;
781
782 register ssize_t
783 i;
784
785 ssize_t
786 y;
787
788 size_t
789 channels;
790
cristy3ed852e2009-09-05 21:47:34 +0000791 /*
792 Open image file.
793 */
794 assert(image_info != (const ImageInfo *) NULL);
795 assert(image_info->signature == MagickSignature);
796 assert(image != (Image *) NULL);
797 assert(image->signature == MagickSignature);
798 if (image->debug != MagickFalse)
799 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7ea34962014-01-04 18:03:30 +0000800 assert(exception != (ExceptionInfo *) NULL);
801 assert(exception->signature == MagickSignature);
cristydb9258a2014-01-03 20:26:19 +0000802 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000803 if (status == MagickFalse)
804 return(status);
805 /*
glennrpbca49a22011-07-01 12:18:22 +0000806 Initialize JPEG 2000 API.
cristy3ed852e2009-09-05 21:47:34 +0000807 */
cristy7ea34962014-01-04 18:03:30 +0000808 opj_set_default_encoder_parameters(&parameters);
809 for (i=1; i < 6; i++)
810 if (((1UL << (i+2)) > image->columns) && ((1UL << (i+2)) > image->rows))
811 break;
812 parameters.numresolution=i;
cristy73bbee12014-01-05 00:43:38 +0000813 option=GetImageOption(image_info,"jp2:number-resolutions");
814 if (option != (const char *) NULL)
815 parameters.numresolution=StringToInteger(option);
cristy7ea34962014-01-04 18:03:30 +0000816 parameters.tcp_numlayers=1;
cristy6d614502014-01-05 14:27:40 +0000817 parameters.tcp_rates[0]=0; /* lossless */
818 parameters.cp_disto_alloc=1;
819 if (image->quality != 0)
820 {
821 parameters.tcp_distoratio[0]=(double) image->quality;
822 parameters.cp_fixed_quality=OPJ_TRUE;
823 }
cristy7ea34962014-01-04 18:03:30 +0000824 if (image_info->extract != (char *) NULL)
825 {
826 RectangleInfo
827 geometry;
828
829 int
830 flags;
831
832 /*
833 Set tile size.
834 */
835 flags=ParseAbsoluteGeometry(image_info->extract,&geometry);
836 parameters.cp_tdx=geometry.width;
837 parameters.cp_tdy=geometry.width;
838 if ((flags & HeightValue) != 0)
839 parameters.cp_tdy=geometry.height;
840 if ((flags & XValue) != 0)
841 parameters.cp_tx0=geometry.x;
842 if ((flags & YValue) != 0)
843 parameters.cp_ty0=geometry.y;
844 parameters.tile_size_on=OPJ_TRUE;
845 }
cristy73bbee12014-01-05 00:43:38 +0000846 option=GetImageOption(image_info,"jp2:quality");
847 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000848 {
849 register const char
850 *p;
851
852 /*
853 Set quality PSNR.
854 */
cristy73bbee12014-01-05 00:43:38 +0000855 p=option;
cristy7ea34962014-01-04 18:03:30 +0000856 for (i=1; sscanf(p,"%f",&parameters.tcp_distoratio[i]) == 1; i++)
857 {
858 if (i > 100)
859 break;
860 while ((*p != '\0') && (*p != ','))
861 p++;
862 if (*p == '\0')
863 break;
864 p++;
865 }
866 parameters.tcp_numlayers=i;
867 parameters.cp_fixed_quality=OPJ_TRUE;
868 }
cristy5fd5a232014-01-05 14:41:50 +0000869 option=GetImageOption(image_info,"jp2:progression-order");
870 if (option != (const char *) NULL)
871 {
872 if (LocaleCompare(option,"LRCP") == 0)
873 parameters.prog_order=OPJ_LRCP;
874 if (LocaleCompare(option,"RLCP") == 0)
875 parameters.prog_order=OPJ_RLCP;
876 if (LocaleCompare(option,"RPCL") == 0)
877 parameters.prog_order=OPJ_RPCL;
878 if (LocaleCompare(option,"PCRL") == 0)
879 parameters.prog_order=OPJ_PCRL;
880 if (LocaleCompare(option,"CPRL") == 0)
881 parameters.prog_order=OPJ_CPRL;
882 }
cristy73bbee12014-01-05 00:43:38 +0000883 option=GetImageOption(image_info,"jp2:rate");
884 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000885 {
886 register const char
887 *p;
888
889 /*
890 Set compression rate.
891 */
cristy73bbee12014-01-05 00:43:38 +0000892 p=option;
cristy7ea34962014-01-04 18:03:30 +0000893 for (i=1; sscanf(p,"%f",&parameters.tcp_rates[i]) == 1; i++)
894 {
895 if (i > 100)
896 break;
897 while ((*p != '\0') && (*p != ','))
898 p++;
899 if (*p == '\0')
900 break;
901 p++;
902 }
903 parameters.tcp_numlayers=i;
904 parameters.cp_disto_alloc=OPJ_TRUE;
905 }
cristy2598c742014-01-05 14:30:44 +0000906 if (image_info->sampling_factor != (const char *) NULL)
907 (void) sscanf(image_info->sampling_factor,"%d,%d",
908 &parameters.subsampling_dx,&parameters.subsampling_dy);
cristy73bbee12014-01-05 00:43:38 +0000909 property=GetImageProperty(image,"comment",exception);
910 if (property != (const char *) NULL)
911 parameters.cp_comment=ConstantString(property);
cristy7ea34962014-01-04 18:03:30 +0000912 channels=3;
913 jp2_colorspace=OPJ_CLRSPC_SRGB;
914 if (image->colorspace == YUVColorspace)
915 {
916 jp2_colorspace=OPJ_CLRSPC_SYCC;
917 parameters.subsampling_dx=2;
918 }
919 else
920 {
921 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
922 (void) TransformImageColorspace(image,sRGBColorspace,exception);
923 if (IsGrayColorspace(image->colorspace) != MagickFalse)
924 {
925 channels=1;
926 jp2_colorspace=OPJ_CLRSPC_GRAY;
927 }
928 if (image->alpha_trait == BlendPixelTrait)
929 channels++;
930 }
cristy6d614502014-01-05 14:27:40 +0000931 parameters.tcp_mct=channels == 3 ? 1 : 0;
cristy7ea34962014-01-04 18:03:30 +0000932 ResetMagickMemory(jp2_info,0,sizeof(jp2_info));
933 for (i=0; i < (ssize_t) channels; i++)
934 {
935 jp2_info[i].prec=image->depth;
936 jp2_info[i].bpp=image->depth;
937 if ((image->depth == 1) &&
938 ((LocaleCompare(image_info->magick,"JPT") == 0) ||
939 (LocaleCompare(image_info->magick,"JP2") == 0)))
940 {
941 jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */
942 jp2_info[i].bpp++;
943 }
944 jp2_info[i].sgnd=0;
945 jp2_info[i].dx=parameters.subsampling_dx;
946 jp2_info[i].dy=parameters.subsampling_dy;
947 jp2_info[i].w=image->columns;
948 jp2_info[i].h=image->rows;
949 }
950 jp2_image=opj_image_create(channels,jp2_info,jp2_colorspace);
951 if (jp2_image == (opj_image_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000952 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
cristy7ea34962014-01-04 18:03:30 +0000953 jp2_image->x0=parameters.image_offset_x0;
954 jp2_image->y0=parameters.image_offset_y0;
955 jp2_image->x1=2*parameters.image_offset_x0+(image->columns-1)*
956 parameters.subsampling_dx+1;
957 jp2_image->y1=2*parameters.image_offset_y0+(image->rows-1)*
958 parameters.subsampling_dx+1;
cristy6d614502014-01-05 14:27:40 +0000959 if ((image->depth == 12) &&
960 ((image->columns == 2048) || (image->rows == 1080) ||
961 (image->columns == 4096) || (image->rows == 2160)))
962 CinemaProfileCompliance(jp2_image,&parameters);
cristy7ea34962014-01-04 18:03:30 +0000963 /*
964 Convert to JP2 pixels.
965 */
966 for (y=0; y < (ssize_t) image->rows; y++)
967 {
968 register const Quantum
969 *p;
970
971 ssize_t
972 x;
973
974 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
975 if (p == (const Quantum *) NULL)
976 break;
977 for (x=0; x < (ssize_t) image->columns; x++)
978 {
979 for (i=0; i < (ssize_t) channels; i++)
980 {
981 double
982 scale;
983
984 register int
985 *q;
986
987 scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange;
988 q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy*
989 image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx);
990 switch (i)
991 {
992 case 0:
993 {
994 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
995 {
996 *q=(int) (scale*GetPixelLuma(image,p));
997 break;
998 }
999 *q=(int) (scale*GetPixelRed(image,p));
1000 break;
1001 }
1002 case 1:
1003 {
1004 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
1005 {
1006 *q=(int) (scale*GetPixelAlpha(image,p));
1007 break;
1008 }
1009 *q=(int) (scale*GetPixelGreen(image,p));
1010 break;
1011 }
1012 case 2:
1013 {
1014 *q=(int) (scale*GetPixelBlue(image,p));
1015 break;
1016 }
1017 case 3:
1018 {
1019 *q=(int) (scale*GetPixelAlpha(image,p));
1020 break;
1021 }
1022 }
1023 }
1024 p++;
1025 }
1026 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1027 image->rows);
1028 if (status == MagickFalse)
1029 break;
1030 }
1031 if (LocaleCompare(image_info->magick,"JPT") == 0)
1032 jp2_codec=opj_create_compress(OPJ_CODEC_JPT);
1033 else
1034 if (LocaleCompare(image_info->magick,"J2K") == 0)
1035 jp2_codec=opj_create_compress(OPJ_CODEC_J2K);
1036 else
1037 jp2_codec=opj_create_compress(OPJ_CODEC_JP2);
1038 opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception);
1039 opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception);
1040 opj_setup_encoder(jp2_codec,&parameters,jp2_image);
1041 jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE);
1042 opj_stream_set_read_function(jp2_stream,JP2ReadHandler);
1043 opj_stream_set_write_function(jp2_stream,JP2WriteHandler);
1044 opj_stream_set_seek_function(jp2_stream,JP2SeekHandler);
1045 opj_stream_set_skip_function(jp2_stream,JP2SkipHandler);
1046 opj_stream_set_user_data(jp2_stream,image);
1047 if (jp2_stream == (opj_stream_t *) NULL)
1048 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1049 jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream);
1050 if (jp2_status == 0)
1051 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1052 if ((opj_encode(jp2_codec,jp2_stream) == 0) ||
1053 (opj_end_compress(jp2_codec,jp2_stream) == 0))
1054 {
1055 opj_stream_set_user_data(jp2_stream,NULL);
1056 opj_stream_destroy_v3(jp2_stream);
1057 opj_destroy_codec(jp2_codec);
1058 opj_image_destroy(jp2_image);
1059 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1060 }
1061 /*
1062 Free resources.
1063 */
1064 opj_stream_set_user_data(jp2_stream,NULL);
1065 opj_stream_destroy_v3(jp2_stream);
1066 opj_destroy_codec(jp2_codec);
1067 opj_image_destroy(jp2_image);
1068 (void) CloseBlob(image);
cristy3ed852e2009-09-05 21:47:34 +00001069 return(MagickTrue);
1070}
1071#endif