blob: d917a9da4fd5bfeaaa652a6a0bd557f92f96bd9d [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% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 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"
cristy191ba5c2014-03-16 21:26:40 +000066#include "MagickCore/semaphore.h"
cristy061f98d2014-01-03 20:19:30 +000067#include "MagickCore/static.h"
68#include "MagickCore/statistic.h"
69#include "MagickCore/string_.h"
70#include "MagickCore/string-private.h"
71#include "MagickCore/module.h"
cristy25997252014-01-02 13:28:18 +000072#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
73#include <openjpeg.h>
cristy3ed852e2009-09-05 21:47:34 +000074#endif
75
76/*
77 Forward declarations.
78*/
cristy25997252014-01-02 13:28:18 +000079#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +000080static MagickBooleanType
cristydb9258a2014-01-03 20:26:19 +000081 WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000082#endif
83
84/*
85%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86% %
87% %
88% %
cristyb5a97912014-01-02 15:52:07 +000089% I s J 2 K %
90% %
91% %
92% %
93%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94%
95% IsJ2K() returns MagickTrue if the image format type, identified by the
96% magick string, is J2K.
97%
98% The format of the IsJ2K method is:
99%
cristy9f138af2014-02-16 12:54:17 +0000100% MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length)
cristyb5a97912014-01-02 15:52:07 +0000101%
102% A description of each parameter follows:
103%
104% o magick: compare image format pattern against these bytes.
105%
106% o length: Specifies the length of the magick string.
107%
108*/
109static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length)
110{
111 if (length < 4)
112 return(MagickFalse);
113 if (memcmp(magick,"\xff\x4f\xff\x51",4) == 0)
114 return(MagickTrue);
115 return(MagickFalse);
116}
117
118/*
119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120% %
121% %
122% %
cristy3ed852e2009-09-05 21:47:34 +0000123% I s J P 2 %
124% %
125% %
126% %
127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128%
129% IsJP2() returns MagickTrue if the image format type, identified by the
130% magick string, is JP2.
131%
132% The format of the IsJP2 method is:
133%
134% MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
135%
136% A description of each parameter follows:
137%
138% o magick: compare image format pattern against these bytes.
139%
140% o length: Specifies the length of the magick string.
141%
142*/
143static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length)
144{
dirk378c9012014-02-16 10:21:39 +0000145 if (length < 4)
146 return(MagickFalse);
147 if (memcmp(magick,"\x0d\x0a\x87\x0a",4) == 0)
148 return(MagickTrue);
cristyb5a97912014-01-02 15:52:07 +0000149 if (length < 12)
cristy3ed852e2009-09-05 21:47:34 +0000150 return(MagickFalse);
cristyb5a97912014-01-02 15:52:07 +0000151 if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0)
152 return(MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000153 return(MagickFalse);
154}
155
156/*
157%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
158% %
159% %
160% %
cristy3ed852e2009-09-05 21:47:34 +0000161% R e a d J P 2 I m a g e %
162% %
163% %
164% %
165%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166%
167% ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000
168% codestream (JPC) image file and returns it. It allocates the memory
169% necessary for the new Image structure and returns a pointer to the new
170% image or set of images.
171%
172% JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu.
173%
174% The format of the ReadJP2Image method is:
175%
176% Image *ReadJP2Image(const ImageInfo *image_info,
177% ExceptionInfo *exception)
178%
179% A description of each parameter follows:
180%
181% o image_info: the image info.
182%
183% o exception: return any errors or warnings in this structure.
184%
185*/
cristy25997252014-01-02 13:28:18 +0000186#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
187static void JP2ErrorHandler(const char *message,void *client_data)
cristy3ed852e2009-09-05 21:47:34 +0000188{
cristy25997252014-01-02 13:28:18 +0000189 ExceptionInfo
190 *exception;
cristy3ed852e2009-09-05 21:47:34 +0000191
cristy25997252014-01-02 13:28:18 +0000192 exception=(ExceptionInfo *) client_data;
193 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
194 message,"`%s'","OpenJP2");
195}
196
197static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context)
198{
cristy3ed852e2009-09-05 21:47:34 +0000199 Image
200 *image;
cristy3ed852e2009-09-05 21:47:34 +0000201
cristy3ed852e2009-09-05 21:47:34 +0000202 ssize_t
203 count;
204
cristy25997252014-01-02 13:28:18 +0000205 image=(Image *) context;
206 count=ReadBlob(image,(ssize_t) length,(unsigned char *) buffer);
cristy530f17f2015-01-09 19:23:27 +0000207 if (count == 0)
cristy10bb01b2014-01-05 17:00:06 +0000208 return((OPJ_SIZE_T) -1);
cristy25997252014-01-02 13:28:18 +0000209 return((OPJ_SIZE_T) count);
cristy3ed852e2009-09-05 21:47:34 +0000210}
211
cristy25997252014-01-02 13:28:18 +0000212static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context)
cristy3ed852e2009-09-05 21:47:34 +0000213{
cristy25997252014-01-02 13:28:18 +0000214 Image
215 *image;
216
217 image=(Image *) context;
cristyc72bbbe2014-01-21 22:10:48 +0000218 return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE);
cristy25997252014-01-02 13:28:18 +0000219}
220
cristydb9258a2014-01-03 20:26:19 +0000221static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context)
cristy25997252014-01-02 13:28:18 +0000222{
223 Image
224 *image;
225
226 image=(Image *) context;
cristyc72bbbe2014-01-21 22:10:48 +0000227 return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset);
cristy25997252014-01-02 13:28:18 +0000228}
229
230static void JP2WarningHandler(const char *message,void *client_data)
231{
232 ExceptionInfo
233 *exception;
234
235 exception=(ExceptionInfo *) client_data;
236 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
237 message,"`%s'","OpenJP2");
238}
239
240static OPJ_SIZE_T JP2WriteHandler(void *buffer,OPJ_SIZE_T length,void *context)
241{
242 Image
243 *image;
244
cristy3ed852e2009-09-05 21:47:34 +0000245 ssize_t
246 count;
247
cristy25997252014-01-02 13:28:18 +0000248 image=(Image *) context;
249 count=WriteBlob(image,(ssize_t) length,(unsigned char *) buffer);
250 return((OPJ_SIZE_T) count);
cristy3ed852e2009-09-05 21:47:34 +0000251}
252
253static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception)
254{
cristy77559f42014-01-02 18:54:04 +0000255 const char
256 *option;
257
cristy3ed852e2009-09-05 21:47:34 +0000258 Image
259 *image;
260
cristy25997252014-01-02 13:28:18 +0000261 int
cristy25997252014-01-02 13:28:18 +0000262 jp2_status;
cristy3ed852e2009-09-05 21:47:34 +0000263
264 MagickBooleanType
265 status;
266
cristy25997252014-01-02 13:28:18 +0000267 opj_codec_t
268 *jp2_codec;
cristy3ed852e2009-09-05 21:47:34 +0000269
cristy6c0f1502014-01-02 13:53:25 +0000270 opj_codestream_index_t
271 *codestream_index = (opj_codestream_index_t *) NULL;
272
cristy25997252014-01-02 13:28:18 +0000273 opj_dparameters_t
274 parameters;
275
276 opj_image_t
277 *jp2_image;
278
279 opj_stream_t
280 *jp2_stream;
cristy48ac1c32012-10-19 23:55:43 +0000281
cristybb503372010-05-27 20:51:26 +0000282 register ssize_t
cristy25997252014-01-02 13:28:18 +0000283 i;
cristy3ed852e2009-09-05 21:47:34 +0000284
cristy524222d2011-04-25 00:37:06 +0000285 ssize_t
cristy524222d2011-04-25 00:37:06 +0000286 y;
287
dirk378c9012014-02-16 10:21:39 +0000288 unsigned char
289 sans[4];
290
cristy3ed852e2009-09-05 21:47:34 +0000291 /*
292 Open image file.
293 */
294 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000295 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000296 if (image_info->debug != MagickFalse)
297 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
298 image_info->filename);
299 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000300 assert(exception->signature == MagickCoreSignature);
cristydb9258a2014-01-03 20:26:19 +0000301 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000302 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
303 if (status == MagickFalse)
304 {
305 image=DestroyImageList(image);
306 return((Image *) NULL);
307 }
308 /*
cristy25997252014-01-02 13:28:18 +0000309 Initialize JP2 codec.
cristy3ed852e2009-09-05 21:47:34 +0000310 */
dirk378c9012014-02-16 10:21:39 +0000311 if (ReadBlob(image,4,sans) != 4)
312 {
313 image=DestroyImageList(image);
314 return((Image *) NULL);
315 }
316 (void) SeekBlob(image,SEEK_SET,0);
cristy25997252014-01-02 13:28:18 +0000317 if (LocaleCompare(image_info->magick,"JPT") == 0)
318 jp2_codec=opj_create_decompress(OPJ_CODEC_JPT);
319 else
dirk536acd12014-02-16 10:38:14 +0000320 if (IsJ2K(sans,4) != MagickFalse)
cristy25997252014-01-02 13:28:18 +0000321 jp2_codec=opj_create_decompress(OPJ_CODEC_J2K);
322 else
323 jp2_codec=opj_create_decompress(OPJ_CODEC_JP2);
324 opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception);
325 opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception);
326 opj_set_default_decoder_parameters(&parameters);
cristya13e3672014-01-02 15:24:44 +0000327 option=GetImageOption(image_info,"jp2:reduce-factor");
328 if (option != (const char *) NULL)
329 parameters.cp_reduce=StringToInteger(option);
cristy7ea34962014-01-04 18:03:30 +0000330 option=GetImageOption(image_info,"jp2:quality-layers");
cristya13e3672014-01-02 15:24:44 +0000331 if (option != (const char *) NULL)
332 parameters.cp_layer=StringToInteger(option);
cristy25997252014-01-02 13:28:18 +0000333 if (opj_setup_decoder(jp2_codec,&parameters) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000334 {
cristy25997252014-01-02 13:28:18 +0000335 opj_destroy_codec(jp2_codec);
336 ThrowReaderException(DelegateError,"UnableToManageJP2Stream");
337 }
cristy66d40562014-01-03 01:27:44 +0000338 jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1);
cristy25997252014-01-02 13:28:18 +0000339 opj_stream_set_read_function(jp2_stream,JP2ReadHandler);
340 opj_stream_set_write_function(jp2_stream,JP2WriteHandler);
341 opj_stream_set_seek_function(jp2_stream,JP2SeekHandler);
342 opj_stream_set_skip_function(jp2_stream,JP2SkipHandler);
cristyb46443e2014-05-08 12:32:16 +0000343 opj_stream_set_user_data(jp2_stream,image,NULL);
cristy25997252014-01-02 13:28:18 +0000344 opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image));
345 if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0)
346 {
cristy3f62cbf2014-04-06 13:02:24 +0000347 opj_stream_destroy(jp2_stream);
cristy25997252014-01-02 13:28:18 +0000348 opj_destroy_codec(jp2_codec);
cristy3ed852e2009-09-05 21:47:34 +0000349 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
350 }
dirk025f9472014-10-25 20:06:53 +0000351 jp2_status=1;
cristya8997c22014-01-03 00:55:02 +0000352 if ((image->columns != 0) && (image->rows != 0))
cristy25997252014-01-02 13:28:18 +0000353 {
cristy56e4faf2014-01-02 17:59:34 +0000354 /*
355 Extract an area from the image.
356 */
dirk39c51eb2014-12-27 01:01:27 +0000357 jp2_status=opj_set_decode_area(jp2_codec,jp2_image,
358 (OPJ_INT32) image->extract_info.x,(OPJ_INT32) image->extract_info.y,
359 (OPJ_INT32) (image->extract_info.x+(ssize_t) image->columns),
360 (OPJ_INT32) (image->extract_info.y+(ssize_t) image->rows));
cristy56e4faf2014-01-02 17:59:34 +0000361 if (jp2_status == 0)
362 {
cristy3f62cbf2014-04-06 13:02:24 +0000363 opj_stream_destroy(jp2_stream);
cristy56e4faf2014-01-02 17:59:34 +0000364 opj_destroy_codec(jp2_codec);
365 opj_image_destroy(jp2_image);
366 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
367 }
cristy25997252014-01-02 13:28:18 +0000368 }
Cristy1142b322015-10-02 17:43:23 -0400369 if ((image_info->number_scenes != 0) && (image_info->scene != 0))
cristy4f976732014-05-09 22:21:21 +0000370 jp2_status=opj_get_decoded_tile(jp2_codec,jp2_stream,jp2_image,
Cristy1142b322015-10-02 17:43:23 -0400371 (unsigned int) image_info->scene-1);
cristy7a4f7322014-10-04 14:41:20 +0000372 else
373 if (image->ping == MagickFalse)
374 {
375 jp2_status=opj_decode(jp2_codec,jp2_stream,jp2_image);
376 if (jp2_status != 0)
377 jp2_status=opj_end_decompress(jp2_codec,jp2_stream);
378 }
cristy4f976732014-05-09 22:21:21 +0000379 if (jp2_status == 0)
cristy25997252014-01-02 13:28:18 +0000380 {
cristy3f62cbf2014-04-06 13:02:24 +0000381 opj_stream_destroy(jp2_stream);
cristy25997252014-01-02 13:28:18 +0000382 opj_destroy_codec(jp2_codec);
383 opj_image_destroy(jp2_image);
384 ThrowReaderException(DelegateError,"UnableToDecodeImageFile");
385 }
cristy3f62cbf2014-04-06 13:02:24 +0000386 opj_stream_destroy(jp2_stream);
cristy25997252014-01-02 13:28:18 +0000387 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
cristy3ed852e2009-09-05 21:47:34 +0000388 {
cristy25997252014-01-02 13:28:18 +0000389 if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0))
cristy3ed852e2009-09-05 21:47:34 +0000390 {
cristy25997252014-01-02 13:28:18 +0000391 opj_destroy_codec(jp2_codec);
392 opj_image_destroy(jp2_image);
393 ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported")
cristy3ed852e2009-09-05 21:47:34 +0000394 }
cristy3ed852e2009-09-05 21:47:34 +0000395 }
396 /*
cristy25997252014-01-02 13:28:18 +0000397 Convert JP2 image.
cristy3ed852e2009-09-05 21:47:34 +0000398 */
cristy25997252014-01-02 13:28:18 +0000399 image->columns=(size_t) jp2_image->comps[0].w;
400 image->rows=(size_t) jp2_image->comps[0].h;
cristyb3445a02014-01-04 01:00:14 +0000401 image->depth=jp2_image->comps[0].prec;
cristyacabb842014-12-14 23:36:33 +0000402 status=SetImageExtent(image,image->columns,image->rows,exception);
403 if (status == MagickFalse)
404 return(DestroyImageList(image));
cristy25997252014-01-02 13:28:18 +0000405 image->compression=JPEG2000Compression;
406 if (jp2_image->numcomps <= 2)
cristy30faca22009-09-29 13:49:52 +0000407 {
cristydb9258a2014-01-03 20:26:19 +0000408 SetImageColorspace(image,GRAYColorspace,exception);
cristy25997252014-01-02 13:28:18 +0000409 if (jp2_image->numcomps > 1)
cristydb9258a2014-01-03 20:26:19 +0000410 image->alpha_trait=BlendPixelTrait;
cristy30faca22009-09-29 13:49:52 +0000411 }
cristy25997252014-01-02 13:28:18 +0000412 if (jp2_image->numcomps > 3)
cristydb9258a2014-01-03 20:26:19 +0000413 image->alpha_trait=BlendPixelTrait;
cristy25997252014-01-02 13:28:18 +0000414 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
cristy25997252014-01-02 13:28:18 +0000415 if ((jp2_image->comps[i].dx > 1) || (jp2_image->comps[i].dy > 1))
cristy18b68a02014-02-13 12:38:47 +0000416 SetImageColorspace(image,YUVColorspace,exception);
cristye173c292014-01-02 13:43:12 +0000417 if (jp2_image->icc_profile_buf != (unsigned char *) NULL)
418 {
419 StringInfo
420 *profile;
421
422 profile=BlobToStringInfo(jp2_image->icc_profile_buf,
423 jp2_image->icc_profile_len);
424 if (profile != (StringInfo *) NULL)
cristydb9258a2014-01-03 20:26:19 +0000425 SetImageProfile(image,"icc",profile,exception);
cristye173c292014-01-02 13:43:12 +0000426 }
dirkbec9e4d2014-09-28 08:33:33 +0000427 if (image->ping != MagickFalse)
428 {
429 opj_destroy_codec(jp2_codec);
430 opj_image_destroy(jp2_image);
431 opj_destroy_cstr_index(&codestream_index);
432 return(GetFirstImageInList(image));
433 }
cristybb503372010-05-27 20:51:26 +0000434 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000435 {
cristydb9258a2014-01-03 20:26:19 +0000436 register Quantum
dirk05d2ff72015-11-18 23:13:43 +0100437 *magick_restrict q;
cristy25997252014-01-02 13:28:18 +0000438
439 register ssize_t
440 x;
441
cristy3ed852e2009-09-05 21:47:34 +0000442 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
cristydb9258a2014-01-03 20:26:19 +0000443 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000444 break;
cristy25997252014-01-02 13:28:18 +0000445 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000446 {
cristy25997252014-01-02 13:28:18 +0000447 register ssize_t
448 i;
449
450 for (i=0; i < (ssize_t) jp2_image->numcomps; i++)
cristy3ed852e2009-09-05 21:47:34 +0000451 {
cristy25997252014-01-02 13:28:18 +0000452 double
453 pixel,
454 scale;
455
456 scale=QuantumRange/(double) ((1UL << jp2_image->comps[i].prec)-1);
457 pixel=scale*(jp2_image->comps[i].data[y/jp2_image->comps[i].dy*
458 image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx]+
459 (jp2_image->comps[i].sgnd ? 1UL << (jp2_image->comps[i].prec-1) : 0));
460 switch (i)
cristy3ed852e2009-09-05 21:47:34 +0000461 {
cristy25997252014-01-02 13:28:18 +0000462 case 0:
463 {
464 SetPixelRed(image,ClampToQuantum(pixel),q);
465 SetPixelGreen(image,ClampToQuantum(pixel),q);
466 SetPixelBlue(image,ClampToQuantum(pixel),q);
467 SetPixelAlpha(image,OpaqueAlpha,q);
468 break;
469 }
470 case 1:
471 {
472 if (jp2_image->numcomps == 2)
473 {
474 SetPixelAlpha(image,ClampToQuantum(pixel),q);
475 break;
476 }
477 SetPixelGreen(image,ClampToQuantum(pixel),q);
478 break;
479 }
480 case 2:
481 {
482 SetPixelBlue(image,ClampToQuantum(pixel),q);
483 break;
484 }
485 case 3:
486 {
487 SetPixelAlpha(image,ClampToQuantum(pixel),q);
488 break;
489 }
cristy3ed852e2009-09-05 21:47:34 +0000490 }
cristy3ed852e2009-09-05 21:47:34 +0000491 }
cristy25997252014-01-02 13:28:18 +0000492 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000493 }
494 if (SyncAuthenticPixels(image,exception) == MagickFalse)
495 break;
cristycee97112010-05-28 00:44:52 +0000496 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy524222d2011-04-25 00:37:06 +0000497 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000498 if (status == MagickFalse)
499 break;
500 }
cristy25997252014-01-02 13:28:18 +0000501 /*
502 Free resources.
503 */
504 opj_destroy_codec(jp2_codec);
505 opj_image_destroy(jp2_image);
cristy6c0f1502014-01-02 13:53:25 +0000506 opj_destroy_cstr_index(&codestream_index);
cristy3ed852e2009-09-05 21:47:34 +0000507 return(GetFirstImageInList(image));
508}
509#endif
510
511/*
512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513% %
514% %
515% %
516% R e g i s t e r J P 2 I m a g e %
517% %
518% %
519% %
520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521%
522% RegisterJP2Image() adds attributes for the JP2 image format to the list of
523% supported formats. The attributes include the image format tag, a method
524% method to read and/or write the format, whether the format supports the
525% saving of more than one frame to the same file or blob, whether the format
526% supports native in-memory I/O, and a brief description of the format.
527%
528% The format of the RegisterJP2Image method is:
529%
cristybb503372010-05-27 20:51:26 +0000530% size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000531%
532*/
cristybb503372010-05-27 20:51:26 +0000533ModuleExport size_t RegisterJP2Image(void)
cristy3ed852e2009-09-05 21:47:34 +0000534{
cristybcd740c2014-01-03 21:08:39 +0000535 char
cristy151b66d2015-04-15 10:50:31 +0000536 version[MagickPathExtent];
cristybcd740c2014-01-03 21:08:39 +0000537
cristy3ed852e2009-09-05 21:47:34 +0000538 MagickInfo
539 *entry;
540
cristybcd740c2014-01-03 21:08:39 +0000541 *version='\0';
542#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy151b66d2015-04-15 10:50:31 +0000543 (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version());
cristybcd740c2014-01-03 21:08:39 +0000544#endif
dirk06b627a2015-04-06 18:59:17 +0000545 entry=AcquireMagickInfo("JP2","JP2","JPEG-2000 File Format Syntax");
cristybcd740c2014-01-03 21:08:39 +0000546 if (*version != '\0')
547 entry->version=ConstantString(version);
cristy5aefbeb2013-08-09 12:13:32 +0000548 entry->mime_type=ConstantString("image/jp2");
cristy3ed852e2009-09-05 21:47:34 +0000549 entry->magick=(IsImageFormatHandler *) IsJP2;
dirk08e9a112015-02-22 01:51:41 +0000550 entry->flags^=CoderAdjoinFlag;
551 entry->flags|=CoderSeekableStreamFlag;
cristy25997252014-01-02 13:28:18 +0000552#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000553 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
554 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
555#endif
556 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000557 entry=AcquireMagickInfo("JP2","J2C","JPEG-2000 Code Stream Syntax");
dirk378c9012014-02-16 10:21:39 +0000558 if (*version != '\0')
559 entry->version=ConstantString(version);
560 entry->mime_type=ConstantString("image/jp2");
dirk378c9012014-02-16 10:21:39 +0000561 entry->magick=(IsImageFormatHandler *) IsJ2K;
dirk08e9a112015-02-22 01:51:41 +0000562 entry->flags^=CoderAdjoinFlag;
563 entry->flags|=CoderSeekableStreamFlag;
dirk378c9012014-02-16 10:21:39 +0000564#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
565 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
566 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
567#endif
568 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000569 entry=AcquireMagickInfo("JP2","J2K","JPEG-2000 Code Stream Syntax");
cristybcd740c2014-01-03 21:08:39 +0000570 if (*version != '\0')
571 entry->version=ConstantString(version);
cristy5aefbeb2013-08-09 12:13:32 +0000572 entry->mime_type=ConstantString("image/jp2");
cristyb5a97912014-01-02 15:52:07 +0000573 entry->magick=(IsImageFormatHandler *) IsJ2K;
dirk08e9a112015-02-22 01:51:41 +0000574 entry->flags^=CoderAdjoinFlag;
575 entry->flags|=CoderSeekableStreamFlag;
cristy25997252014-01-02 13:28:18 +0000576#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy9bfeb942012-05-10 12:07:32 +0000577 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
578 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
579#endif
580 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000581 entry=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format Syntax");
cristy2267fa42014-09-12 10:27:47 +0000582 if (*version != '\0')
583 entry->version=ConstantString(version);
584 entry->mime_type=ConstantString("image/jp2");
cristy2267fa42014-09-12 10:27:47 +0000585 entry->magick=(IsImageFormatHandler *) IsJP2;
dirk08e9a112015-02-22 01:51:41 +0000586 entry->flags^=CoderAdjoinFlag;
587 entry->flags|=CoderSeekableStreamFlag;
cristy2267fa42014-09-12 10:27:47 +0000588#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
589 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
590 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
591#endif
592 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000593 entry=AcquireMagickInfo("JP2","JPT","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");
cristy25997252014-01-02 13:28:18 +0000597 entry->magick=(IsImageFormatHandler *) IsJP2;
dirk08e9a112015-02-22 01:51:41 +0000598 entry->flags^=CoderAdjoinFlag;
599 entry->flags|=CoderSeekableStreamFlag;
cristy25997252014-01-02 13:28:18 +0000600#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000601 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
602 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
603#endif
604 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000605 entry=AcquireMagickInfo("JP2","JPC","JPEG-2000 Code Stream Syntax");
cristybcd740c2014-01-03 21:08:39 +0000606 if (*version != '\0')
607 entry->version=ConstantString(version);
cristy92f84a52014-01-02 15:10:12 +0000608 entry->mime_type=ConstantString("image/jp2");
cristy9f138af2014-02-16 12:54:17 +0000609 entry->magick=(IsImageFormatHandler *) IsJP2;
dirk08e9a112015-02-22 01:51:41 +0000610 entry->flags^=CoderAdjoinFlag;
611 entry->flags|=CoderSeekableStreamFlag;
cristy92f84a52014-01-02 15:10:12 +0000612#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
613 entry->decoder=(DecodeImageHandler *) ReadJP2Image;
614 entry->encoder=(EncodeImageHandler *) WriteJP2Image;
615#endif
616 (void) RegisterMagickInfo(entry);
cristy3ed852e2009-09-05 21:47:34 +0000617 return(MagickImageCoderSignature);
618}
619
620/*
621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622% %
623% %
624% %
625% U n r e g i s t e r J P 2 I m a g e %
626% %
627% %
628% %
629%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630%
631% UnregisterJP2Image() removes format registrations made by the JP2 module
632% from the list of supported formats.
633%
634% The format of the UnregisterJP2Image method is:
635%
636% UnregisterJP2Image(void)
637%
638*/
639ModuleExport void UnregisterJP2Image(void)
640{
cristy92f84a52014-01-02 15:10:12 +0000641 (void) UnregisterMagickInfo("JPC");
cristy25997252014-01-02 13:28:18 +0000642 (void) UnregisterMagickInfo("JPT");
cristy2267fa42014-09-12 10:27:47 +0000643 (void) UnregisterMagickInfo("JPM");
cristy02f1fda2009-12-10 15:23:56 +0000644 (void) UnregisterMagickInfo("JP2");
cristy25997252014-01-02 13:28:18 +0000645 (void) UnregisterMagickInfo("J2K");
cristy3ed852e2009-09-05 21:47:34 +0000646}
647
cristy25997252014-01-02 13:28:18 +0000648#if defined(MAGICKCORE_LIBOPENJP2_DELEGATE)
cristy3ed852e2009-09-05 21:47:34 +0000649/*
650%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651% %
652% %
653% %
654% W r i t e J P 2 I m a g e %
655% %
656% %
657% %
658%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659%
660% WriteJP2Image() writes an image in the JPEG 2000 image format.
661%
662% JP2 support originally written by Nathan Brown, nathanbrown@letu.edu
663%
664% The format of the WriteJP2Image method is:
665%
cristydb9258a2014-01-03 20:26:19 +0000666% MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
667% ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000668%
669% A description of each parameter follows.
670%
671% o image_info: the image info.
672%
673% o image: The image.
674%
675*/
cristy6d614502014-01-05 14:27:40 +0000676
677static void CinemaProfileCompliance(const opj_image_t *jp2_image,
678 opj_cparameters_t *parameters)
679{
680 /*
cristy121d6602014-01-05 14:35:51 +0000681 Digital Cinema 4K profile compliant codestream.
cristy6d614502014-01-05 14:27:40 +0000682 */
683 parameters->tile_size_on=OPJ_FALSE;
684 parameters->cp_tdx=1;
685 parameters->cp_tdy=1;
686 parameters->tp_flag='C';
687 parameters->tp_on=1;
688 parameters->cp_tx0=0;
689 parameters->cp_ty0=0;
690 parameters->image_offset_x0=0;
691 parameters->image_offset_y0=0;
692 parameters->cblockw_init=32;
693 parameters->cblockh_init=32;
694 parameters->csty|=0x01;
695 parameters->prog_order=OPJ_CPRL;
696 parameters->roi_compno=(-1);
697 parameters->subsampling_dx=1;
698 parameters->subsampling_dy=1;
699 parameters->irreversible=1;
700 if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080))
701 {
702 /*
703 Digital Cinema 2K.
704 */
705 parameters->cp_cinema=OPJ_CINEMA2K_24;
706 parameters->cp_rsiz=OPJ_CINEMA2K;
707 parameters->max_comp_size=1041666;
708 if (parameters->numresolution > 6)
709 parameters->numresolution=6;
710
711 }
712 if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160))
713 {
714 /*
715 Digital Cinema 4K.
716 */
717 parameters->cp_cinema=OPJ_CINEMA4K_24;
718 parameters->cp_rsiz=OPJ_CINEMA4K;
719 parameters->max_comp_size=1041666;
720 if (parameters->numresolution < 1)
721 parameters->numresolution=1;
722 if (parameters->numresolution > 7)
723 parameters->numresolution=7;
724 parameters->numpocs=2;
725 parameters->POC[0].tile=1;
726 parameters->POC[0].resno0=0;
727 parameters->POC[0].compno0=0;
728 parameters->POC[0].layno1=1;
729 parameters->POC[0].resno1=parameters->numresolution-1;
730 parameters->POC[0].compno1=3;
731 parameters->POC[0].prg1=OPJ_CPRL;
732 parameters->POC[1].tile=1;
733 parameters->POC[1].resno0=parameters->numresolution-1;
734 parameters->POC[1].compno0=0;
735 parameters->POC[1].layno1=1;
736 parameters->POC[1].resno1=parameters->numresolution;
737 parameters->POC[1].compno1=3;
738 parameters->POC[1].prg1=OPJ_CPRL;
739 }
740 parameters->tcp_numlayers=1;
741 parameters->tcp_rates[0]=((float) (jp2_image->numcomps*jp2_image->comps[0].w*
742 jp2_image->comps[0].h*jp2_image->comps[0].prec))/(parameters->max_comp_size*
743 8*jp2_image->comps[0].dx*jp2_image->comps[0].dy);
744 parameters->cp_disto_alloc=1;
745}
746
cristydb9258a2014-01-03 20:26:19 +0000747static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image,
748 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000749{
cristy7ea34962014-01-04 18:03:30 +0000750 const char
cristy73bbee12014-01-05 00:43:38 +0000751 *option,
752 *property;
cristy7ea34962014-01-04 18:03:30 +0000753
754 int
755 jp2_status;
756
cristy3ed852e2009-09-05 21:47:34 +0000757 MagickBooleanType
758 status;
759
cristy7ea34962014-01-04 18:03:30 +0000760 opj_codec_t
761 *jp2_codec;
762
763 OPJ_COLOR_SPACE
764 jp2_colorspace;
765
766 opj_cparameters_t
767 parameters;
768
769 opj_image_cmptparm_t
770 jp2_info[5];
771
772 opj_image_t
773 *jp2_image;
774
775 opj_stream_t
776 *jp2_stream;
777
778 register ssize_t
779 i;
780
781 ssize_t
782 y;
783
cristy3b0cc212015-07-18 12:11:30 +0000784 unsigned int
cristy7ea34962014-01-04 18:03:30 +0000785 channels;
786
cristy3ed852e2009-09-05 21:47:34 +0000787 /*
788 Open image file.
789 */
790 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000791 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000792 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000793 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000794 if (image->debug != MagickFalse)
795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy7ea34962014-01-04 18:03:30 +0000796 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000797 assert(exception->signature == MagickCoreSignature);
cristydb9258a2014-01-03 20:26:19 +0000798 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000799 if (status == MagickFalse)
800 return(status);
801 /*
glennrpbca49a22011-07-01 12:18:22 +0000802 Initialize JPEG 2000 API.
cristy3ed852e2009-09-05 21:47:34 +0000803 */
cristy7ea34962014-01-04 18:03:30 +0000804 opj_set_default_encoder_parameters(&parameters);
805 for (i=1; i < 6; i++)
dirk1678aec2015-08-30 12:07:29 +0200806 if (((size_t) (1UL << (i+2)) > image->columns) &&
807 ((size_t) (1UL << (i+2)) > image->rows))
cristy7ea34962014-01-04 18:03:30 +0000808 break;
809 parameters.numresolution=i;
cristy73bbee12014-01-05 00:43:38 +0000810 option=GetImageOption(image_info,"jp2:number-resolutions");
811 if (option != (const char *) NULL)
812 parameters.numresolution=StringToInteger(option);
cristy7ea34962014-01-04 18:03:30 +0000813 parameters.tcp_numlayers=1;
cristy6d614502014-01-05 14:27:40 +0000814 parameters.tcp_rates[0]=0; /* lossless */
815 parameters.cp_disto_alloc=1;
Cristy857e74a2015-08-25 18:31:02 -0400816 if ((image_info->quality != 0) && (image_info->quality != 100))
cristy6d614502014-01-05 14:27:40 +0000817 {
dirkcbb1fff2015-08-25 22:23:19 +0200818 parameters.tcp_distoratio[0]=(double) image_info->quality;
cristy6d614502014-01-05 14:27:40 +0000819 parameters.cp_fixed_quality=OPJ_TRUE;
820 }
cristy7ea34962014-01-04 18:03:30 +0000821 if (image_info->extract != (char *) NULL)
822 {
823 RectangleInfo
824 geometry;
825
826 int
827 flags;
828
829 /*
830 Set tile size.
831 */
832 flags=ParseAbsoluteGeometry(image_info->extract,&geometry);
cristydabb4d82014-10-25 17:51:16 +0000833 parameters.cp_tdx=(int) geometry.width;
834 parameters.cp_tdy=(int) geometry.width;
cristy7ea34962014-01-04 18:03:30 +0000835 if ((flags & HeightValue) != 0)
cristydabb4d82014-10-25 17:51:16 +0000836 parameters.cp_tdy=(int) geometry.height;
cristy7ea34962014-01-04 18:03:30 +0000837 if ((flags & XValue) != 0)
838 parameters.cp_tx0=geometry.x;
839 if ((flags & YValue) != 0)
840 parameters.cp_ty0=geometry.y;
841 parameters.tile_size_on=OPJ_TRUE;
842 }
cristy73bbee12014-01-05 00:43:38 +0000843 option=GetImageOption(image_info,"jp2:quality");
844 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000845 {
846 register const char
847 *p;
848
849 /*
850 Set quality PSNR.
851 */
cristy73bbee12014-01-05 00:43:38 +0000852 p=option;
cristy21e84852014-01-12 19:29:02 +0000853 for (i=0; sscanf(p,"%f",&parameters.tcp_distoratio[i]) == 1; i++)
cristy7ea34962014-01-04 18:03:30 +0000854 {
855 if (i > 100)
856 break;
857 while ((*p != '\0') && (*p != ','))
858 p++;
859 if (*p == '\0')
860 break;
861 p++;
862 }
cristy21e84852014-01-12 19:29:02 +0000863 parameters.tcp_numlayers=i+1;
cristy7ea34962014-01-04 18:03:30 +0000864 parameters.cp_fixed_quality=OPJ_TRUE;
865 }
cristy5fd5a232014-01-05 14:41:50 +0000866 option=GetImageOption(image_info,"jp2:progression-order");
867 if (option != (const char *) NULL)
868 {
869 if (LocaleCompare(option,"LRCP") == 0)
870 parameters.prog_order=OPJ_LRCP;
871 if (LocaleCompare(option,"RLCP") == 0)
872 parameters.prog_order=OPJ_RLCP;
873 if (LocaleCompare(option,"RPCL") == 0)
874 parameters.prog_order=OPJ_RPCL;
875 if (LocaleCompare(option,"PCRL") == 0)
876 parameters.prog_order=OPJ_PCRL;
877 if (LocaleCompare(option,"CPRL") == 0)
878 parameters.prog_order=OPJ_CPRL;
879 }
cristy73bbee12014-01-05 00:43:38 +0000880 option=GetImageOption(image_info,"jp2:rate");
881 if (option != (const char *) NULL)
cristy7ea34962014-01-04 18:03:30 +0000882 {
883 register const char
884 *p;
885
886 /*
887 Set compression rate.
888 */
cristy73bbee12014-01-05 00:43:38 +0000889 p=option;
cristy21e84852014-01-12 19:29:02 +0000890 for (i=0; sscanf(p,"%f",&parameters.tcp_rates[i]) == 1; i++)
cristy7ea34962014-01-04 18:03:30 +0000891 {
cristyf2a82ee2014-05-26 17:49:54 +0000892 if (i >= 100)
cristy7ea34962014-01-04 18:03:30 +0000893 break;
894 while ((*p != '\0') && (*p != ','))
895 p++;
896 if (*p == '\0')
897 break;
898 p++;
899 }
cristy21e84852014-01-12 19:29:02 +0000900 parameters.tcp_numlayers=i+1;
cristy7ea34962014-01-04 18:03:30 +0000901 parameters.cp_disto_alloc=OPJ_TRUE;
902 }
cristy2598c742014-01-05 14:30:44 +0000903 if (image_info->sampling_factor != (const char *) NULL)
904 (void) sscanf(image_info->sampling_factor,"%d,%d",
905 &parameters.subsampling_dx,&parameters.subsampling_dy);
cristy73bbee12014-01-05 00:43:38 +0000906 property=GetImageProperty(image,"comment",exception);
907 if (property != (const char *) NULL)
908 parameters.cp_comment=ConstantString(property);
cristy7ea34962014-01-04 18:03:30 +0000909 channels=3;
910 jp2_colorspace=OPJ_CLRSPC_SRGB;
911 if (image->colorspace == YUVColorspace)
912 {
913 jp2_colorspace=OPJ_CLRSPC_SYCC;
914 parameters.subsampling_dx=2;
915 }
916 else
917 {
cristy7ea34962014-01-04 18:03:30 +0000918 if (IsGrayColorspace(image->colorspace) != MagickFalse)
919 {
920 channels=1;
921 jp2_colorspace=OPJ_CLRSPC_GRAY;
922 }
dirk7b6ffff2015-09-16 14:08:55 +0200923 else
924 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy17f11b02014-12-20 19:37:04 +0000925 if (image->alpha_trait != UndefinedPixelTrait)
cristy7ea34962014-01-04 18:03:30 +0000926 channels++;
927 }
cristy6d614502014-01-05 14:27:40 +0000928 parameters.tcp_mct=channels == 3 ? 1 : 0;
cristy7ea34962014-01-04 18:03:30 +0000929 ResetMagickMemory(jp2_info,0,sizeof(jp2_info));
930 for (i=0; i < (ssize_t) channels; i++)
931 {
dirk39c51eb2014-12-27 01:01:27 +0000932 jp2_info[i].prec=(OPJ_UINT32) image->depth;
933 jp2_info[i].bpp=(OPJ_UINT32) image->depth;
cristy7ea34962014-01-04 18:03:30 +0000934 if ((image->depth == 1) &&
935 ((LocaleCompare(image_info->magick,"JPT") == 0) ||
936 (LocaleCompare(image_info->magick,"JP2") == 0)))
937 {
938 jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */
939 jp2_info[i].bpp++;
940 }
941 jp2_info[i].sgnd=0;
942 jp2_info[i].dx=parameters.subsampling_dx;
943 jp2_info[i].dy=parameters.subsampling_dy;
dirk39c51eb2014-12-27 01:01:27 +0000944 jp2_info[i].w=(OPJ_UINT32) image->columns;
945 jp2_info[i].h=(OPJ_UINT32) image->rows;
cristy7ea34962014-01-04 18:03:30 +0000946 }
dirk39c51eb2014-12-27 01:01:27 +0000947 jp2_image=opj_image_create((OPJ_UINT32) channels,jp2_info,jp2_colorspace);
cristy7ea34962014-01-04 18:03:30 +0000948 if (jp2_image == (opj_image_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000949 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
cristy7ea34962014-01-04 18:03:30 +0000950 jp2_image->x0=parameters.image_offset_x0;
951 jp2_image->y0=parameters.image_offset_y0;
cristydabb4d82014-10-25 17:51:16 +0000952 jp2_image->x1=(unsigned int) (2*parameters.image_offset_x0+(image->columns-1)*
953 parameters.subsampling_dx+1);
954 jp2_image->y1=(unsigned int) (2*parameters.image_offset_y0+(image->rows-1)*
955 parameters.subsampling_dx+1);
cristy6d614502014-01-05 14:27:40 +0000956 if ((image->depth == 12) &&
957 ((image->columns == 2048) || (image->rows == 1080) ||
958 (image->columns == 4096) || (image->rows == 2160)))
959 CinemaProfileCompliance(jp2_image,&parameters);
cristy9e0f31c2015-04-07 01:27:46 +0000960 if (channels == 4)
961 jp2_image->comps[3].alpha=1;
962 else
963 if ((channels == 2) && (jp2_colorspace == OPJ_CLRSPC_GRAY))
964 jp2_image->comps[1].alpha=1;
cristy7ea34962014-01-04 18:03:30 +0000965 /*
966 Convert to JP2 pixels.
967 */
968 for (y=0; y < (ssize_t) image->rows; y++)
969 {
970 register const Quantum
971 *p;
972
973 ssize_t
974 x;
975
976 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
977 if (p == (const Quantum *) NULL)
978 break;
979 for (x=0; x < (ssize_t) image->columns; x++)
980 {
981 for (i=0; i < (ssize_t) channels; i++)
982 {
983 double
984 scale;
985
986 register int
987 *q;
988
989 scale=(double) ((1UL << jp2_image->comps[i].prec)-1)/QuantumRange;
990 q=jp2_image->comps[i].data+(y/jp2_image->comps[i].dy*
991 image->columns/jp2_image->comps[i].dx+x/jp2_image->comps[i].dx);
992 switch (i)
993 {
994 case 0:
995 {
996 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
997 {
998 *q=(int) (scale*GetPixelLuma(image,p));
999 break;
1000 }
1001 *q=(int) (scale*GetPixelRed(image,p));
1002 break;
1003 }
1004 case 1:
1005 {
1006 if (jp2_colorspace == OPJ_CLRSPC_GRAY)
1007 {
1008 *q=(int) (scale*GetPixelAlpha(image,p));
1009 break;
1010 }
1011 *q=(int) (scale*GetPixelGreen(image,p));
1012 break;
1013 }
1014 case 2:
1015 {
1016 *q=(int) (scale*GetPixelBlue(image,p));
1017 break;
1018 }
1019 case 3:
1020 {
1021 *q=(int) (scale*GetPixelAlpha(image,p));
1022 break;
1023 }
1024 }
1025 }
cristy5ecce992014-01-12 16:01:44 +00001026 p+=GetPixelChannels(image);
cristy7ea34962014-01-04 18:03:30 +00001027 }
1028 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1029 image->rows);
1030 if (status == MagickFalse)
1031 break;
1032 }
1033 if (LocaleCompare(image_info->magick,"JPT") == 0)
1034 jp2_codec=opj_create_compress(OPJ_CODEC_JPT);
1035 else
1036 if (LocaleCompare(image_info->magick,"J2K") == 0)
1037 jp2_codec=opj_create_compress(OPJ_CODEC_J2K);
1038 else
1039 jp2_codec=opj_create_compress(OPJ_CODEC_JP2);
1040 opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception);
1041 opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception);
1042 opj_setup_encoder(jp2_codec,&parameters,jp2_image);
1043 jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE);
1044 opj_stream_set_read_function(jp2_stream,JP2ReadHandler);
1045 opj_stream_set_write_function(jp2_stream,JP2WriteHandler);
1046 opj_stream_set_seek_function(jp2_stream,JP2SeekHandler);
1047 opj_stream_set_skip_function(jp2_stream,JP2SkipHandler);
cristyb46443e2014-05-08 12:32:16 +00001048 opj_stream_set_user_data(jp2_stream,image,NULL);
cristy7ea34962014-01-04 18:03:30 +00001049 if (jp2_stream == (opj_stream_t *) NULL)
1050 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1051 jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream);
1052 if (jp2_status == 0)
1053 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1054 if ((opj_encode(jp2_codec,jp2_stream) == 0) ||
1055 (opj_end_compress(jp2_codec,jp2_stream) == 0))
1056 {
cristy3f62cbf2014-04-06 13:02:24 +00001057 opj_stream_destroy(jp2_stream);
cristy7ea34962014-01-04 18:03:30 +00001058 opj_destroy_codec(jp2_codec);
1059 opj_image_destroy(jp2_image);
1060 ThrowWriterException(DelegateError,"UnableToEncodeImageFile");
1061 }
1062 /*
1063 Free resources.
1064 */
cristy3f62cbf2014-04-06 13:02:24 +00001065 opj_stream_destroy(jp2_stream);
cristy7ea34962014-01-04 18:03:30 +00001066 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