/src/imagemagick/coders/jp2.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % JJJ PPPP 222 % |
7 | | % J P P 2 2 % |
8 | | % J PPPP 22 % |
9 | | % J J P 2 % |
10 | | % JJ P 22222 % |
11 | | % % |
12 | | % % |
13 | | % Read/Write JPEG-2000 Image Format % |
14 | | % % |
15 | | % Cristy % |
16 | | % Nathan Brown % |
17 | | % June 2001 % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % 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 | | % https://imagemagick.org/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | */ |
38 | | |
39 | | /* |
40 | | Include declarations. |
41 | | */ |
42 | | #include "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-private.h" |
64 | | #include "MagickCore/property.h" |
65 | | #include "MagickCore/quantum-private.h" |
66 | | #include "MagickCore/resource_.h" |
67 | | #include "MagickCore/semaphore.h" |
68 | | #include "MagickCore/static.h" |
69 | | #include "MagickCore/statistic.h" |
70 | | #include "MagickCore/string_.h" |
71 | | #include "MagickCore/string-private.h" |
72 | | #include "MagickCore/module.h" |
73 | | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
74 | | #include <openjpeg.h> |
75 | | |
76 | | #define OPJ_COMPUTE_NUMERIC_VERSION(major,minor,patch) ((major<<24) | (minor<<16) | (patch<<8) | 0) |
77 | | #define OPJ_NUMERIC_VERSION OPJ_COMPUTE_NUMERIC_VERSION(OPJ_VERSION_MAJOR,OPJ_VERSION_MINOR,OPJ_VERSION_BUILD) |
78 | | #endif |
79 | | |
80 | | /* |
81 | | Typedef declarations. |
82 | | */ |
83 | | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
84 | | typedef struct _JP2CompsInfo |
85 | | { |
86 | | double |
87 | | scale; |
88 | | |
89 | | ssize_t |
90 | | addition, |
91 | | pad, |
92 | | y_index; |
93 | | } JP2CompsInfo; |
94 | | #endif |
95 | | |
96 | | /* |
97 | | Forward declarations. |
98 | | */ |
99 | | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
100 | | static MagickBooleanType |
101 | | WriteJP2Image(const ImageInfo *,Image *,ExceptionInfo *); |
102 | | #endif |
103 | | |
104 | | /* |
105 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
106 | | % % |
107 | | % % |
108 | | % % |
109 | | % I s J 2 K % |
110 | | % % |
111 | | % % |
112 | | % % |
113 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
114 | | % |
115 | | % IsJ2K() returns MagickTrue if the image format type, identified by the |
116 | | % magick string, is J2K. |
117 | | % |
118 | | % The format of the IsJ2K method is: |
119 | | % |
120 | | % MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) |
121 | | % |
122 | | % A description of each parameter follows: |
123 | | % |
124 | | % o magick: compare image format pattern against these bytes. |
125 | | % |
126 | | % o length: Specifies the length of the magick string. |
127 | | % |
128 | | */ |
129 | | static MagickBooleanType IsJ2K(const unsigned char *magick,const size_t length) |
130 | 35.4k | { |
131 | 35.4k | if (length < 4) |
132 | 0 | return(MagickFalse); |
133 | 35.4k | if (memcmp(magick,"\xff\x4f\xff\x51",4) == 0) |
134 | 30.5k | return(MagickTrue); |
135 | 4.89k | return(MagickFalse); |
136 | 35.4k | } |
137 | | |
138 | | /* |
139 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
140 | | % % |
141 | | % % |
142 | | % % |
143 | | % I s J P 2 % |
144 | | % % |
145 | | % % |
146 | | % % |
147 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
148 | | % |
149 | | % IsJP2() returns MagickTrue if the image format type, identified by the |
150 | | % magick string, is JP2. |
151 | | % |
152 | | % The format of the IsJP2 method is: |
153 | | % |
154 | | % MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) |
155 | | % |
156 | | % A description of each parameter follows: |
157 | | % |
158 | | % o magick: compare image format pattern against these bytes. |
159 | | % |
160 | | % o length: Specifies the length of the magick string. |
161 | | % |
162 | | */ |
163 | | static MagickBooleanType IsJP2(const unsigned char *magick,const size_t length) |
164 | 4.89k | { |
165 | 4.89k | if (length < 4) |
166 | 0 | return(MagickFalse); |
167 | 4.89k | if (memcmp(magick,"\x0d\x0a\x87\x0a",4) == 0) |
168 | 440 | return(MagickTrue); |
169 | 4.45k | if (length < 12) |
170 | 0 | return(MagickFalse); |
171 | 4.45k | if (memcmp(magick,"\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a",12) == 0) |
172 | 4.21k | return(MagickTrue); |
173 | 236 | return(MagickFalse); |
174 | 4.45k | } |
175 | | |
176 | | /* |
177 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
178 | | % % |
179 | | % % |
180 | | % % |
181 | | % R e a d J P 2 I m a g e % |
182 | | % % |
183 | | % % |
184 | | % % |
185 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
186 | | % |
187 | | % ReadJP2Image() reads a JPEG 2000 Image file (JP2) or JPEG 2000 |
188 | | % codestream (JPC) image file and returns it. It allocates the memory |
189 | | % necessary for the new Image structure and returns a pointer to the new |
190 | | % image or set of images. |
191 | | % |
192 | | % JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu. |
193 | | % |
194 | | % The format of the ReadJP2Image method is: |
195 | | % |
196 | | % Image *ReadJP2Image(const ImageInfo *image_info, |
197 | | % ExceptionInfo *exception) |
198 | | % |
199 | | % A description of each parameter follows: |
200 | | % |
201 | | % o image_info: the image info. |
202 | | % |
203 | | % o exception: return any errors or warnings in this structure. |
204 | | % |
205 | | */ |
206 | | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
207 | | static void JP2ErrorHandler(const char *message,void *client_data) |
208 | 39.5k | { |
209 | 39.5k | ExceptionInfo |
210 | 39.5k | *exception; |
211 | | |
212 | 39.5k | exception=(ExceptionInfo *) client_data; |
213 | 39.5k | (void) ThrowMagickException(exception,GetMagickModule(),CoderError, |
214 | 39.5k | message,"`%s'","OpenJP2"); |
215 | 39.5k | } |
216 | | |
217 | | static OPJ_SIZE_T JP2ReadHandler(void *buffer,OPJ_SIZE_T length,void *context) |
218 | 41.1k | { |
219 | 41.1k | Image |
220 | 41.1k | *image; |
221 | | |
222 | 41.1k | ssize_t |
223 | 41.1k | count; |
224 | | |
225 | 41.1k | image=(Image *) context; |
226 | 41.1k | count=ReadBlob(image,(size_t) length,(unsigned char *) buffer); |
227 | 41.1k | if (count == 0) |
228 | 5.30k | return((OPJ_SIZE_T) -1); |
229 | 35.8k | return((OPJ_SIZE_T) count); |
230 | 41.1k | } |
231 | | |
232 | | static OPJ_BOOL JP2SeekHandler(OPJ_OFF_T offset,void *context) |
233 | 14.2k | { |
234 | 14.2k | Image |
235 | 14.2k | *image; |
236 | | |
237 | 14.2k | image=(Image *) context; |
238 | 14.2k | return(SeekBlob(image,offset,SEEK_SET) < 0 ? OPJ_FALSE : OPJ_TRUE); |
239 | 14.2k | } |
240 | | |
241 | | static OPJ_OFF_T JP2SkipHandler(OPJ_OFF_T offset,void *context) |
242 | 5.65k | { |
243 | 5.65k | Image |
244 | 5.65k | *image; |
245 | | |
246 | 5.65k | image=(Image *) context; |
247 | 5.65k | return(SeekBlob(image,offset,SEEK_CUR) < 0 ? -1 : offset); |
248 | 5.65k | } |
249 | | |
250 | | static void JP2WarningHandler(const char *message,void *client_data) |
251 | 4.62M | { |
252 | 4.62M | ExceptionInfo |
253 | 4.62M | *exception; |
254 | | |
255 | 4.62M | exception=(ExceptionInfo *) client_data; |
256 | 4.62M | (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning, |
257 | 4.62M | message,"`%s'","OpenJP2"); |
258 | 4.62M | } |
259 | | |
260 | | static OPJ_SIZE_T JP2WriteHandler(void *buffer,OPJ_SIZE_T length,void *context) |
261 | 20.6k | { |
262 | 20.6k | Image |
263 | 20.6k | *image; |
264 | | |
265 | 20.6k | ssize_t |
266 | 20.6k | count; |
267 | | |
268 | 20.6k | image=(Image *) context; |
269 | 20.6k | count=WriteBlob(image,(size_t) length,(unsigned char *) buffer); |
270 | 20.6k | return((OPJ_SIZE_T) count); |
271 | 20.6k | } |
272 | | |
273 | | static MagickBooleanType JP2ComponentHasAlpha(const ImageInfo* image_info, |
274 | | opj_image_comp_t comp) |
275 | 649 | { |
276 | 649 | const char |
277 | 649 | *option; |
278 | | |
279 | 649 | if (comp.alpha != 0) |
280 | 114 | return(MagickTrue); |
281 | 535 | option=GetImageOption(image_info,"jp2:assume-alpha"); |
282 | 535 | return(IsStringTrue(option)); |
283 | 649 | } |
284 | | |
285 | | static Image *ReadJP2Image(const ImageInfo *image_info,ExceptionInfo *exception) |
286 | 35.5k | { |
287 | 35.5k | const char |
288 | 35.5k | *option; |
289 | | |
290 | 35.5k | Image |
291 | 35.5k | *image; |
292 | | |
293 | 35.5k | int |
294 | 35.5k | jp2_status; |
295 | | |
296 | 35.5k | JP2CompsInfo |
297 | 35.5k | comps_info[MaxPixelChannels]; |
298 | | |
299 | 35.5k | MagickBooleanType |
300 | 35.5k | status; |
301 | | |
302 | 35.5k | opj_codec_t |
303 | 35.5k | *jp2_codec; |
304 | | |
305 | 35.5k | opj_codestream_info_v2_t |
306 | 35.5k | *jp2_codestream_info; |
307 | | |
308 | 35.5k | opj_dparameters_t |
309 | 35.5k | parameters; |
310 | | |
311 | 35.5k | opj_image_t |
312 | 35.5k | *jp2_image; |
313 | | |
314 | 35.5k | opj_stream_t |
315 | 35.5k | *jp2_stream; |
316 | | |
317 | 35.5k | ssize_t |
318 | 35.5k | i, |
319 | 35.5k | y; |
320 | | |
321 | 35.5k | unsigned char |
322 | 35.5k | magick[16]; |
323 | | |
324 | | /* |
325 | | Open image file. |
326 | | */ |
327 | 35.5k | assert(image_info != (const ImageInfo *) NULL); |
328 | 35.5k | assert(image_info->signature == MagickCoreSignature); |
329 | 35.5k | assert(exception != (ExceptionInfo *) NULL); |
330 | 35.5k | assert(exception->signature == MagickCoreSignature); |
331 | 35.5k | if (IsEventLogging() != MagickFalse) |
332 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
333 | 0 | image_info->filename); |
334 | 35.5k | image=AcquireImage(image_info,exception); |
335 | 35.5k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
336 | 35.5k | if (status == MagickFalse) |
337 | 0 | { |
338 | 0 | image=DestroyImageList(image); |
339 | 0 | return((Image *) NULL); |
340 | 0 | } |
341 | | /* |
342 | | Initialize JP2 codec. |
343 | | */ |
344 | 35.5k | if (ReadBlob(image,sizeof(magick),magick) != sizeof(magick)) |
345 | 34 | { |
346 | 34 | image=DestroyImageList(image); |
347 | 34 | return((Image *) NULL); |
348 | 34 | } |
349 | 35.5k | (void) SeekBlob(image,SEEK_SET,0); |
350 | 35.5k | if (LocaleCompare(image_info->magick,"JPT") == 0) |
351 | 34 | jp2_codec=opj_create_decompress(OPJ_CODEC_JPT); |
352 | 35.4k | else |
353 | 35.4k | if (IsJ2K(magick,sizeof(magick)) != MagickFalse) |
354 | 30.5k | jp2_codec=opj_create_decompress(OPJ_CODEC_J2K); |
355 | 4.89k | else |
356 | 4.89k | if (IsJP2(magick,sizeof(magick)) != MagickFalse) |
357 | 4.65k | jp2_codec=opj_create_decompress(OPJ_CODEC_JP2); |
358 | 236 | else |
359 | 35.2k | ThrowReaderException(DelegateError,"UnableToManageJP2Stream"); |
360 | 35.2k | opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); |
361 | 35.2k | opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); |
362 | 35.2k | opj_set_default_decoder_parameters(¶meters); |
363 | 35.2k | option=GetImageOption(image_info,"jp2:reduce-factor"); |
364 | 35.2k | if (option != (const char *) NULL) |
365 | 0 | parameters.cp_reduce=(unsigned int) StringToInteger(option); |
366 | 35.2k | option=GetImageOption(image_info,"jp2:quality-layers"); |
367 | 35.2k | if (option != (const char *) NULL) |
368 | 0 | parameters.cp_layer=(unsigned int) StringToInteger(option); |
369 | 35.2k | if (opj_setup_decoder(jp2_codec,¶meters) == 0) |
370 | 34 | { |
371 | 34 | opj_destroy_codec(jp2_codec); |
372 | 34 | ThrowReaderException(DelegateError,"UnableToManageJP2Stream"); |
373 | 0 | } |
374 | 35.2k | jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,1); |
375 | 35.2k | opj_stream_set_read_function(jp2_stream,JP2ReadHandler); |
376 | 35.2k | opj_stream_set_write_function(jp2_stream,JP2WriteHandler); |
377 | 35.2k | opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); |
378 | 35.2k | opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); |
379 | 35.2k | opj_stream_set_user_data(jp2_stream,image,NULL); |
380 | 35.2k | opj_stream_set_user_data_length(jp2_stream,GetBlobSize(image)); |
381 | 35.2k | jp2_image=(opj_image_t *) NULL; |
382 | 35.2k | if (opj_read_header(jp2_stream,jp2_codec,&jp2_image) == 0) |
383 | 8.43k | { |
384 | 8.43k | opj_stream_destroy(jp2_stream); |
385 | 8.43k | opj_destroy_codec(jp2_codec); |
386 | 8.43k | ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); |
387 | 0 | } |
388 | 26.7k | jp2_codestream_info=opj_get_cstr_info(jp2_codec); |
389 | 26.7k | if (AcquireMagickResource(ListLengthResource,(MagickSizeType) |
390 | 26.7k | jp2_codestream_info->m_default_tile_info.numlayers) == MagickFalse) |
391 | 240 | { |
392 | 240 | opj_destroy_cstr_info(&jp2_codestream_info); |
393 | 240 | opj_stream_destroy(jp2_stream); |
394 | 240 | opj_destroy_codec(jp2_codec); |
395 | 240 | opj_image_destroy(jp2_image); |
396 | 240 | ThrowReaderException(ResourceLimitError,"ListLengthExceedsLimit"); |
397 | 0 | } |
398 | 26.5k | opj_destroy_cstr_info(&jp2_codestream_info); |
399 | 26.5k | jp2_status=OPJ_TRUE; |
400 | 26.5k | if ((AcquireMagickResource(WidthResource,(size_t) jp2_image->comps[0].w) == MagickFalse) || |
401 | 26.2k | (AcquireMagickResource(WidthResource,(size_t) jp2_image->x1) == MagickFalse) || |
402 | 26.2k | (AcquireMagickResource(HeightResource,(size_t) jp2_image->comps[0].h) == MagickFalse) || |
403 | 26.1k | (AcquireMagickResource(HeightResource,(size_t) jp2_image->y1) == MagickFalse)) |
404 | 380 | { |
405 | 380 | opj_stream_destroy(jp2_stream); |
406 | 380 | opj_destroy_codec(jp2_codec); |
407 | 380 | opj_image_destroy(jp2_image); |
408 | 380 | ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); |
409 | 0 | } |
410 | 26.1k | if (image->ping == MagickFalse) |
411 | 26.1k | { |
412 | 26.1k | if ((image->columns != 0) && (image->rows != 0)) |
413 | | /* |
414 | | Extract an area from the image. |
415 | | */ |
416 | 0 | jp2_status=opj_set_decode_area(jp2_codec,jp2_image, |
417 | 0 | (OPJ_INT32) image->extract_info.x,(OPJ_INT32) image->extract_info.y, |
418 | 0 | (OPJ_INT32) (image->extract_info.x+(ssize_t) image->columns), |
419 | 0 | (OPJ_INT32) (image->extract_info.y+(ssize_t) image->rows)); |
420 | 26.1k | else |
421 | 26.1k | jp2_status=opj_set_decode_area(jp2_codec,jp2_image,0,0, |
422 | 26.1k | (int) jp2_image->comps[0].w,(int) jp2_image->comps[0].h); |
423 | 26.1k | if (jp2_status == OPJ_FALSE) |
424 | 17 | { |
425 | 17 | opj_stream_destroy(jp2_stream); |
426 | 17 | opj_destroy_codec(jp2_codec); |
427 | 17 | opj_image_destroy(jp2_image); |
428 | 17 | ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); |
429 | 0 | } |
430 | 26.1k | } |
431 | 26.1k | if ((image_info->number_scenes != 0) && (image_info->scene != 0)) |
432 | 0 | jp2_status=opj_get_decoded_tile(jp2_codec,jp2_stream,jp2_image, |
433 | 0 | (unsigned int) image_info->scene-1); |
434 | 26.1k | else |
435 | 26.1k | if (image->ping == MagickFalse) |
436 | 26.1k | { |
437 | 26.1k | jp2_status=opj_decode(jp2_codec,jp2_stream,jp2_image); |
438 | 26.1k | if (jp2_status != OPJ_FALSE) |
439 | 19.4k | jp2_status=opj_end_decompress(jp2_codec,jp2_stream); |
440 | 26.1k | } |
441 | 26.1k | if (jp2_status == OPJ_FALSE) |
442 | 6.70k | { |
443 | 6.70k | opj_stream_destroy(jp2_stream); |
444 | 6.70k | opj_destroy_codec(jp2_codec); |
445 | 6.70k | opj_image_destroy(jp2_image); |
446 | 6.70k | ThrowReaderException(DelegateError,"UnableToDecodeImageFile"); |
447 | 0 | } |
448 | 19.4k | if (jp2_image->numcomps >= MaxPixelChannels) |
449 | 23 | { |
450 | 23 | opj_stream_destroy(jp2_stream); |
451 | 23 | opj_destroy_codec(jp2_codec); |
452 | 23 | opj_image_destroy(jp2_image); |
453 | 23 | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
454 | 0 | } |
455 | 42.2k | for (i=0; i < (ssize_t) jp2_image->numcomps; i++) |
456 | 22.9k | { |
457 | 22.9k | if ((jp2_image->comps[i].dx == 0) || (jp2_image->comps[i].dy == 0) || |
458 | 22.9k | (jp2_image->comps[0].prec != jp2_image->comps[i].prec) || |
459 | 22.8k | (jp2_image->comps[0].prec > 64) || |
460 | 22.8k | (jp2_image->comps[0].sgnd != jp2_image->comps[i].sgnd)) |
461 | 154 | { |
462 | 154 | opj_stream_destroy(jp2_stream); |
463 | 154 | opj_destroy_codec(jp2_codec); |
464 | 154 | opj_image_destroy(jp2_image); |
465 | 154 | ThrowReaderException(CoderError,"IrregularChannelGeometryNotSupported") |
466 | 154 | } |
467 | 22.9k | } |
468 | 19.2k | opj_stream_destroy(jp2_stream); |
469 | 19.2k | if (image->ping == MagickFalse) |
470 | 19.2k | { |
471 | 41.8k | for (i=0; i < (ssize_t) jp2_image->numcomps; i++) |
472 | 22.5k | if (jp2_image->comps[i].data == NULL) |
473 | 0 | { |
474 | 0 | opj_destroy_codec(jp2_codec); |
475 | 0 | opj_image_destroy(jp2_image); |
476 | 0 | ThrowReaderException(CoderError, |
477 | 0 | "IrregularChannelGeometryNotSupported") |
478 | 0 | } |
479 | 19.2k | } |
480 | | /* |
481 | | Convert JP2 image. |
482 | | */ |
483 | 19.2k | image->columns=(size_t) jp2_image->comps[0].w; |
484 | 19.2k | image->rows=(size_t) jp2_image->comps[0].h; |
485 | 19.2k | image->depth=jp2_image->comps[0].prec; |
486 | 19.2k | image->compression=JPEG2000Compression; |
487 | 19.2k | if (jp2_image->numcomps == 1) |
488 | 18.0k | SetImageColorspace(image,GRAYColorspace,exception); |
489 | 1.18k | else |
490 | 1.18k | if (jp2_image->color_space == 2) |
491 | 53 | SetImageColorspace(image,GRAYColorspace,exception); |
492 | 1.13k | else |
493 | 1.13k | if (jp2_image->color_space == 3) |
494 | 166 | SetImageColorspace(image,Rec601YCbCrColorspace,exception); |
495 | 19.2k | if (jp2_image->numcomps > 3) |
496 | 517 | { |
497 | 517 | size_t |
498 | 517 | number_meta_channels; |
499 | | |
500 | 517 | number_meta_channels=jp2_image->numcomps-3; |
501 | 517 | if (JP2ComponentHasAlpha(image_info,jp2_image->comps[3]) != MagickFalse) |
502 | 60 | { |
503 | 60 | image->alpha_trait=BlendPixelTrait; |
504 | 60 | number_meta_channels-=1; |
505 | 60 | } |
506 | 517 | if (number_meta_channels > 0) |
507 | 493 | { |
508 | 493 | status=SetPixelMetaChannels(image,(size_t) number_meta_channels, |
509 | 493 | exception); |
510 | 493 | if (status == MagickFalse) |
511 | 0 | { |
512 | 0 | opj_destroy_codec(jp2_codec); |
513 | 0 | opj_image_destroy(jp2_image); |
514 | 0 | return(DestroyImageList(image)); |
515 | 0 | } |
516 | 493 | } |
517 | 517 | } |
518 | 18.7k | else if (jp2_image->numcomps == 2) |
519 | 132 | { |
520 | 132 | if (JP2ComponentHasAlpha(image_info,jp2_image->comps[1]) != MagickFalse) |
521 | 54 | image->alpha_trait=BlendPixelTrait; |
522 | 132 | } |
523 | 18.6k | else if ((jp2_image->numcomps == 1) && (jp2_image->comps[0].alpha != 0)) |
524 | 18 | image->alpha_trait=BlendPixelTrait; |
525 | 19.2k | if (jp2_image->icc_profile_buf != (unsigned char *) NULL) |
526 | 347 | { |
527 | 347 | StringInfo |
528 | 347 | *profile; |
529 | | |
530 | 347 | profile=BlobToProfileStringInfo("icc",jp2_image->icc_profile_buf, |
531 | 347 | jp2_image->icc_profile_len,exception); |
532 | 347 | (void) SetImageProfilePrivate(image,profile,exception); |
533 | 347 | } |
534 | 19.2k | if (image->ping != MagickFalse) |
535 | 7 | { |
536 | 7 | opj_destroy_codec(jp2_codec); |
537 | 7 | opj_image_destroy(jp2_image); |
538 | 7 | return(GetFirstImageInList(image)); |
539 | 7 | } |
540 | 19.2k | status=SetImageExtent(image,image->columns,image->rows,exception); |
541 | 19.2k | if (status == MagickFalse) |
542 | 0 | { |
543 | 0 | opj_destroy_codec(jp2_codec); |
544 | 0 | opj_image_destroy(jp2_image); |
545 | 0 | return(DestroyImageList(image)); |
546 | 0 | } |
547 | 19.2k | memset(comps_info,0,MaxPixelChannels*sizeof(JP2CompsInfo)); |
548 | 41.8k | for (i=0; i < (ssize_t) jp2_image->numcomps; i++) |
549 | 22.5k | { |
550 | 22.5k | comps_info[i].scale=(double) QuantumRange/(double) |
551 | 22.5k | ((MagickULLConstant(1) << jp2_image->comps[i].prec)-1); |
552 | 22.5k | comps_info[i].addition=(jp2_image->comps[i].sgnd ? |
553 | 20.8k | MagickULLConstant(1) << (jp2_image->comps[i].prec-1) : 0); |
554 | 22.5k | comps_info[i].pad=(ssize_t) image->columns % jp2_image->comps[i].dx; |
555 | 22.5k | } |
556 | 7.64M | for (y=0; y < (ssize_t) image->rows; y++) |
557 | 7.62M | { |
558 | 7.62M | Quantum |
559 | 7.62M | *magick_restrict q; |
560 | | |
561 | 7.62M | ssize_t |
562 | 7.62M | x; |
563 | | |
564 | 15.6M | for (i=0; i < (ssize_t) jp2_image->numcomps; i++) |
565 | 8.01M | { |
566 | 8.01M | comps_info[i].y_index=y/jp2_image->comps[i].dy*((ssize_t) image->columns+ |
567 | 8.01M | comps_info[i].pad); |
568 | 8.01M | } |
569 | 7.62M | q=GetAuthenticPixels(image,0,y,image->columns,1,exception); |
570 | 7.62M | if (q == (Quantum *) NULL) |
571 | 0 | break; |
572 | 7.57G | for (x=0; x < (ssize_t) image->columns; x++) |
573 | 7.56G | { |
574 | 15.3G | for (i=0; i < (ssize_t) jp2_image->numcomps; i++) |
575 | 7.75G | { |
576 | 7.75G | double |
577 | 7.75G | pixel; |
578 | | |
579 | 7.75G | ssize_t |
580 | 7.75G | index; |
581 | | |
582 | 7.75G | index=comps_info[i].y_index/jp2_image->comps[i].dx+x/ |
583 | 7.75G | jp2_image->comps[i].dx; |
584 | 7.75G | if ((index < 0) || |
585 | 7.75G | (index >= (ssize_t) (jp2_image->comps[i].h*jp2_image->comps[i].w))) |
586 | 140 | { |
587 | 140 | opj_destroy_codec(jp2_codec); |
588 | 140 | opj_image_destroy(jp2_image); |
589 | 140 | ThrowReaderException(CoderError, |
590 | 140 | "IrregularChannelGeometryNotSupported") |
591 | 140 | } |
592 | 7.75G | pixel=comps_info[i].scale*(jp2_image->comps[i].data[index]+ |
593 | 7.75G | comps_info[i].addition); |
594 | 7.75G | switch (i) |
595 | 7.75G | { |
596 | 7.56G | case 0: |
597 | 7.56G | { |
598 | 7.56G | if (jp2_image->numcomps == 1) |
599 | 7.48G | { |
600 | 7.48G | SetPixelGray(image,ClampToQuantum(pixel),q); |
601 | 7.48G | if (jp2_image->comps[i].alpha != 0) |
602 | 654 | SetPixelAlpha(image,ClampToQuantum(pixel),q); |
603 | 7.48G | break; |
604 | 7.48G | } |
605 | 82.9M | SetPixelRed(image,ClampToQuantum(pixel),q); |
606 | 82.9M | if (jp2_image->numcomps == 2) |
607 | 206k | { |
608 | 206k | SetPixelGreen(image,ClampToQuantum(pixel),q); |
609 | 206k | SetPixelBlue(image,ClampToQuantum(pixel),q); |
610 | 206k | } |
611 | 82.9M | break; |
612 | 7.56G | } |
613 | 82.9M | case 1: |
614 | 82.9M | { |
615 | 82.9M | if ((jp2_image->numcomps == 2) && (jp2_image->comps[i].alpha != 0)) |
616 | 8.44k | { |
617 | 8.44k | SetPixelAlpha(image,ClampToQuantum(pixel),q); |
618 | 8.44k | break; |
619 | 8.44k | } |
620 | 82.9M | SetPixelGreen(image,ClampToQuantum(pixel),q); |
621 | 82.9M | break; |
622 | 82.9M | } |
623 | 82.7M | case 2: |
624 | 82.7M | { |
625 | 82.7M | SetPixelBlue(image,ClampToQuantum(pixel),q); |
626 | 82.7M | break; |
627 | 82.9M | } |
628 | 18.9M | case 3: |
629 | 18.9M | { |
630 | 18.9M | if ((image->alpha_trait & BlendPixelTrait) != 0) |
631 | 83.6k | SetPixelAlpha(image,ClampToQuantum(pixel),q); |
632 | 18.8M | else |
633 | 18.8M | SetPixelChannel(image,MetaPixelChannels,ClampToQuantum(pixel),q); |
634 | 18.9M | break; |
635 | 82.9M | } |
636 | 1.64M | default: |
637 | 1.64M | { |
638 | 1.64M | ssize_t |
639 | 1.64M | offset = MetaPixelChannels+i-3; |
640 | | |
641 | 1.64M | if ((image->alpha_trait & BlendPixelTrait) != 0) |
642 | 79.4k | offset--; |
643 | 1.64M | SetPixelChannel(image,(PixelChannel) offset,ClampToQuantum(pixel), |
644 | 1.64M | q); |
645 | 1.64M | break; |
646 | 82.9M | } |
647 | 7.75G | } |
648 | 7.75G | } |
649 | 7.56G | q+=(ptrdiff_t) GetPixelChannels(image); |
650 | 7.56G | } |
651 | 7.62M | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
652 | 0 | break; |
653 | 7.62M | status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, |
654 | 7.62M | image->rows); |
655 | 7.62M | if (status == MagickFalse) |
656 | 0 | break; |
657 | 7.62M | } |
658 | | /* |
659 | | Free resources. |
660 | | */ |
661 | 19.1k | opj_destroy_codec(jp2_codec); |
662 | 19.1k | opj_image_destroy(jp2_image); |
663 | 19.1k | (void) CloseBlob(image); |
664 | 19.1k | if ((image_info->number_scenes != 0) && (image_info->scene != 0)) |
665 | 0 | AppendImageToList(&image,CloneImage(image,0,0,MagickTrue,exception)); |
666 | 19.1k | return(GetFirstImageInList(image)); |
667 | 19.2k | } |
668 | | #endif |
669 | | |
670 | | /* |
671 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
672 | | % % |
673 | | % % |
674 | | % % |
675 | | % R e g i s t e r J P 2 I m a g e % |
676 | | % % |
677 | | % % |
678 | | % % |
679 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
680 | | % |
681 | | % RegisterJP2Image() adds attributes for the JP2 image format to the list of |
682 | | % supported formats. The attributes include the image format tag, a method |
683 | | % method to read and/or write the format, whether the format supports the |
684 | | % saving of more than one frame to the same file or blob, whether the format |
685 | | % supports native in-memory I/O, and a brief description of the format. |
686 | | % |
687 | | % The format of the RegisterJP2Image method is: |
688 | | % |
689 | | % size_t RegisterJP2Image(void) |
690 | | % |
691 | | */ |
692 | | ModuleExport size_t RegisterJP2Image(void) |
693 | 15 | { |
694 | 15 | char |
695 | 15 | version[MagickPathExtent]; |
696 | | |
697 | 15 | MagickInfo |
698 | 15 | *entry; |
699 | | |
700 | 15 | *version='\0'; |
701 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
702 | 15 | (void) FormatLocaleString(version,MagickPathExtent,"%s",opj_version()); |
703 | 15 | #endif |
704 | 15 | entry=AcquireMagickInfo("JP2","JP2","JPEG-2000 File Format Syntax"); |
705 | 15 | if (*version != '\0') |
706 | 15 | entry->version=ConstantString(version); |
707 | 15 | entry->mime_type=ConstantString("image/jp2"); |
708 | 15 | entry->magick=(IsImageFormatHandler *) IsJP2; |
709 | 15 | entry->flags^=CoderAdjoinFlag; |
710 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
711 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
712 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
713 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
714 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
715 | 15 | #endif |
716 | 15 | (void) RegisterMagickInfo(entry); |
717 | 15 | entry=AcquireMagickInfo("JP2","J2C","JPEG-2000 Code Stream Syntax"); |
718 | 15 | if (*version != '\0') |
719 | 15 | entry->version=ConstantString(version); |
720 | 15 | entry->mime_type=ConstantString("image/jp2"); |
721 | 15 | entry->magick=(IsImageFormatHandler *) IsJ2K; |
722 | 15 | entry->flags^=CoderAdjoinFlag; |
723 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
724 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
725 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
726 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
727 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
728 | 15 | #endif |
729 | 15 | (void) RegisterMagickInfo(entry); |
730 | 15 | entry=AcquireMagickInfo("JP2","J2K","JPEG-2000 Code Stream Syntax"); |
731 | 15 | if (*version != '\0') |
732 | 15 | entry->version=ConstantString(version); |
733 | 15 | entry->mime_type=ConstantString("image/jp2"); |
734 | 15 | entry->magick=(IsImageFormatHandler *) IsJ2K; |
735 | 15 | entry->flags^=CoderAdjoinFlag; |
736 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
737 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
738 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
739 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
740 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
741 | 15 | #endif |
742 | 15 | (void) RegisterMagickInfo(entry); |
743 | 15 | entry=AcquireMagickInfo("JP2","JPM","JPEG-2000 File Format Syntax"); |
744 | 15 | if (*version != '\0') |
745 | 15 | entry->version=ConstantString(version); |
746 | 15 | entry->mime_type=ConstantString("image/jp2"); |
747 | 15 | entry->magick=(IsImageFormatHandler *) IsJP2; |
748 | 15 | entry->flags^=CoderAdjoinFlag; |
749 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
750 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
751 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
752 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
753 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
754 | 15 | #endif |
755 | 15 | (void) RegisterMagickInfo(entry); |
756 | 15 | entry=AcquireMagickInfo("JP2","JPT","JPEG-2000 File Format Syntax"); |
757 | 15 | if (*version != '\0') |
758 | 15 | entry->version=ConstantString(version); |
759 | 15 | entry->mime_type=ConstantString("image/jp2"); |
760 | 15 | entry->magick=(IsImageFormatHandler *) IsJP2; |
761 | 15 | entry->flags^=CoderAdjoinFlag; |
762 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
763 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
764 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
765 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
766 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
767 | 15 | #endif |
768 | 15 | (void) RegisterMagickInfo(entry); |
769 | 15 | entry=AcquireMagickInfo("JP2","JPC","JPEG-2000 Code Stream Syntax"); |
770 | 15 | if (*version != '\0') |
771 | 15 | entry->version=ConstantString(version); |
772 | 15 | entry->mime_type=ConstantString("image/jp2"); |
773 | 15 | entry->magick=(IsImageFormatHandler *) IsJP2; |
774 | 15 | entry->flags^=CoderAdjoinFlag; |
775 | 15 | entry->flags|=CoderDecoderSeekableStreamFlag; |
776 | 15 | entry->flags|=CoderEncoderSeekableStreamFlag; |
777 | 15 | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
778 | 15 | entry->decoder=(DecodeImageHandler *) ReadJP2Image; |
779 | 15 | entry->encoder=(EncodeImageHandler *) WriteJP2Image; |
780 | 15 | #endif |
781 | 15 | (void) RegisterMagickInfo(entry); |
782 | 15 | return(MagickImageCoderSignature); |
783 | 15 | } |
784 | | |
785 | | /* |
786 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
787 | | % % |
788 | | % % |
789 | | % % |
790 | | % U n r e g i s t e r J P 2 I m a g e % |
791 | | % % |
792 | | % % |
793 | | % % |
794 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
795 | | % |
796 | | % UnregisterJP2Image() removes format registrations made by the JP2 module |
797 | | % from the list of supported formats. |
798 | | % |
799 | | % The format of the UnregisterJP2Image method is: |
800 | | % |
801 | | % UnregisterJP2Image(void) |
802 | | % |
803 | | */ |
804 | | ModuleExport void UnregisterJP2Image(void) |
805 | 0 | { |
806 | 0 | (void) UnregisterMagickInfo("JPC"); |
807 | 0 | (void) UnregisterMagickInfo("JPT"); |
808 | 0 | (void) UnregisterMagickInfo("JPM"); |
809 | 0 | (void) UnregisterMagickInfo("JP2"); |
810 | 0 | (void) UnregisterMagickInfo("J2K"); |
811 | 0 | } |
812 | | |
813 | | #if defined(MAGICKCORE_LIBOPENJP2_DELEGATE) |
814 | | /* |
815 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
816 | | % % |
817 | | % % |
818 | | % % |
819 | | % W r i t e J P 2 I m a g e % |
820 | | % % |
821 | | % % |
822 | | % % |
823 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
824 | | % |
825 | | % WriteJP2Image() writes an image in the JPEG 2000 image format. |
826 | | % |
827 | | % JP2 support originally written by Nathan Brown, nathanbrown@letu.edu |
828 | | % |
829 | | % The format of the WriteJP2Image method is: |
830 | | % |
831 | | % MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, |
832 | | % ExceptionInfo *exception) |
833 | | % |
834 | | % A description of each parameter follows. |
835 | | % |
836 | | % o image_info: the image info. |
837 | | % |
838 | | % o image: The image. |
839 | | % |
840 | | */ |
841 | | |
842 | | static void CinemaProfileCompliance(const opj_image_t *jp2_image, |
843 | | opj_cparameters_t *parameters) |
844 | 814 | { |
845 | | /* |
846 | | Digital Cinema 4K profile compliant codestream. |
847 | | */ |
848 | 814 | parameters->tile_size_on=OPJ_FALSE; |
849 | 814 | parameters->cp_tdx=1; |
850 | 814 | parameters->cp_tdy=1; |
851 | 814 | parameters->tp_flag='C'; |
852 | 814 | parameters->tp_on=1; |
853 | 814 | parameters->cp_tx0=0; |
854 | 814 | parameters->cp_ty0=0; |
855 | 814 | parameters->image_offset_x0=0; |
856 | 814 | parameters->image_offset_y0=0; |
857 | 814 | parameters->cblockw_init=32; |
858 | 814 | parameters->cblockh_init=32; |
859 | 814 | parameters->csty|=0x01; |
860 | 814 | parameters->prog_order=OPJ_CPRL; |
861 | 814 | parameters->roi_compno=(-1); |
862 | 814 | parameters->subsampling_dx=1; |
863 | 814 | parameters->subsampling_dy=1; |
864 | 814 | parameters->irreversible=1; |
865 | 814 | if ((jp2_image->comps[0].w == 2048) || (jp2_image->comps[0].h == 1080)) |
866 | 814 | { |
867 | | /* |
868 | | Digital Cinema 2K. |
869 | | */ |
870 | 814 | #if OPJ_NUMERIC_VERSION >= OPJ_COMPUTE_NUMERIC_VERSION(2,1,0) |
871 | 814 | parameters->rsiz=OPJ_PROFILE_CINEMA_2K; |
872 | 814 | parameters->max_cs_size=OPJ_CINEMA_24_CS; |
873 | 814 | parameters->max_comp_size=OPJ_CINEMA_24_COMP; |
874 | | #else |
875 | | parameters->cp_cinema=OPJ_CINEMA2K_24; |
876 | | parameters->cp_rsiz=OPJ_CINEMA2K; |
877 | | parameters->max_comp_size=1041666; |
878 | | #endif |
879 | 814 | if (parameters->numresolution > 6) |
880 | 0 | parameters->numresolution=6; |
881 | | |
882 | 814 | } |
883 | 814 | if ((jp2_image->comps[0].w == 4096) || (jp2_image->comps[0].h == 2160)) |
884 | 0 | { |
885 | | /* |
886 | | Digital Cinema 4K. |
887 | | */ |
888 | 0 | #if OPJ_NUMERIC_VERSION >= OPJ_COMPUTE_NUMERIC_VERSION(2,1,0) |
889 | 0 | parameters->rsiz=OPJ_PROFILE_CINEMA_4K; |
890 | 0 | parameters->max_cs_size=OPJ_CINEMA_24_CS; |
891 | 0 | parameters->max_comp_size=OPJ_CINEMA_24_COMP; |
892 | | #else |
893 | | parameters->cp_cinema=OPJ_CINEMA4K_24; |
894 | | parameters->cp_rsiz=OPJ_CINEMA4K; |
895 | | parameters->max_comp_size=1041666; |
896 | | #endif |
897 | 0 | if (parameters->numresolution < 1) |
898 | 0 | parameters->numresolution=1; |
899 | 0 | if (parameters->numresolution > 7) |
900 | 0 | parameters->numresolution=7; |
901 | 0 | parameters->numpocs=2; |
902 | 0 | parameters->POC[0].tile=1; |
903 | 0 | parameters->POC[0].resno0=0; |
904 | 0 | parameters->POC[0].compno0=0; |
905 | 0 | parameters->POC[0].layno1=1; |
906 | 0 | parameters->POC[0].resno1=(unsigned int) parameters->numresolution-1; |
907 | 0 | parameters->POC[0].compno1=3; |
908 | 0 | parameters->POC[0].prg1=OPJ_CPRL; |
909 | 0 | parameters->POC[1].tile=1; |
910 | 0 | parameters->POC[1].resno0=(unsigned int) parameters->numresolution-1; |
911 | 0 | parameters->POC[1].compno0=0; |
912 | 0 | parameters->POC[1].layno1=1; |
913 | 0 | parameters->POC[1].resno1=(unsigned int) parameters->numresolution; |
914 | 0 | parameters->POC[1].compno1=3; |
915 | 0 | parameters->POC[1].prg1=OPJ_CPRL; |
916 | 0 | } |
917 | 814 | parameters->tcp_numlayers=1; |
918 | 814 | parameters->tcp_rates[0]=(((float) jp2_image->numcomps*jp2_image->comps[0].w* |
919 | 814 | jp2_image->comps[0].h*jp2_image->comps[0].prec))/((float) |
920 | 814 | parameters->max_comp_size*8*jp2_image->comps[0].dx*jp2_image->comps[0].dy); |
921 | 814 | parameters->cp_disto_alloc=OPJ_TRUE; |
922 | 814 | } |
923 | | |
924 | | static inline int CalculateNumResolutions(size_t width,size_t height) |
925 | 8.48k | { |
926 | 8.48k | int |
927 | 8.48k | i; |
928 | | |
929 | 41.7k | for (i=1; i < 6; i++) |
930 | 35.8k | if ((width < ((size_t) MagickULLConstant(1) << i)) || |
931 | 34.3k | (height < ((size_t) MagickULLConstant(1) << i))) |
932 | 2.56k | break; |
933 | 8.48k | return(i); |
934 | 8.48k | } |
935 | | |
936 | | static MagickBooleanType WriteJP2Image(const ImageInfo *image_info,Image *image, |
937 | | ExceptionInfo *exception) |
938 | 8.48k | { |
939 | 8.48k | const char |
940 | 8.48k | *option, |
941 | 8.48k | *property; |
942 | | |
943 | 8.48k | int |
944 | 8.48k | jp2_status; |
945 | | |
946 | 8.48k | MagickBooleanType |
947 | 8.48k | status; |
948 | | |
949 | 8.48k | opj_codec_t |
950 | 8.48k | *jp2_codec; |
951 | | |
952 | 8.48k | OPJ_COLOR_SPACE |
953 | 8.48k | jp2_colorspace; |
954 | | |
955 | 8.48k | opj_cparameters_t |
956 | 8.48k | *parameters; |
957 | | |
958 | 8.48k | opj_image_cmptparm_t |
959 | 8.48k | jp2_info[5]; |
960 | | |
961 | 8.48k | opj_image_t |
962 | 8.48k | *jp2_image; |
963 | | |
964 | 8.48k | opj_stream_t |
965 | 8.48k | *jp2_stream; |
966 | | |
967 | 8.48k | ssize_t |
968 | 8.48k | i, |
969 | 8.48k | y; |
970 | | |
971 | 8.48k | unsigned int |
972 | 8.48k | channels; |
973 | | |
974 | | /* |
975 | | Open image file. |
976 | | */ |
977 | 8.48k | assert(image_info != (const ImageInfo *) NULL); |
978 | 8.48k | assert(image_info->signature == MagickCoreSignature); |
979 | 8.48k | assert(image != (Image *) NULL); |
980 | 8.48k | assert(image->signature == MagickCoreSignature); |
981 | 8.48k | assert(exception != (ExceptionInfo *) NULL); |
982 | 8.48k | assert(exception->signature == MagickCoreSignature); |
983 | 8.48k | if (IsEventLogging() != MagickFalse) |
984 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
985 | 8.48k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
986 | 8.48k | if (status == MagickFalse) |
987 | 0 | return(status); |
988 | | /* |
989 | | Initialize JPEG 2000 API. |
990 | | */ |
991 | 8.48k | parameters=(opj_cparameters_t *) AcquireMagickMemory(sizeof(*parameters)); |
992 | 8.48k | if (parameters == (opj_cparameters_t *) NULL) |
993 | 8.48k | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
994 | 8.48k | opj_set_default_encoder_parameters(parameters); |
995 | 8.48k | option=GetImageOption(image_info,"jp2:number-resolutions"); |
996 | 8.48k | if (option != (const char *) NULL) |
997 | 0 | parameters->numresolution=StringToInteger(option); |
998 | 8.48k | else |
999 | 8.48k | parameters->numresolution=CalculateNumResolutions(image->columns, |
1000 | 8.48k | image->rows); |
1001 | 8.48k | parameters->tcp_numlayers=1; |
1002 | 8.48k | parameters->tcp_rates[0]=0; /* lossless */ |
1003 | 8.48k | parameters->cp_disto_alloc=OPJ_TRUE; |
1004 | 8.48k | if ((image_info->quality != 0) && (image_info->quality != 100)) |
1005 | 0 | { |
1006 | 0 | parameters->tcp_distoratio[0]=(float) image_info->quality; |
1007 | 0 | parameters->cp_fixed_quality=OPJ_TRUE; |
1008 | 0 | parameters->cp_disto_alloc=OPJ_FALSE; |
1009 | 0 | } |
1010 | 8.48k | if (image_info->extract != (char *) NULL) |
1011 | 0 | { |
1012 | 0 | int |
1013 | 0 | flags; |
1014 | |
|
1015 | 0 | RectangleInfo |
1016 | 0 | geometry; |
1017 | | |
1018 | | /* |
1019 | | Set tile size. |
1020 | | */ |
1021 | 0 | (void) memset(&geometry,0,sizeof(geometry)); |
1022 | 0 | flags=(int) ParseAbsoluteGeometry(image_info->extract,&geometry); |
1023 | 0 | parameters->cp_tdx=(int) geometry.width; |
1024 | 0 | parameters->cp_tdy=(int) geometry.width; |
1025 | 0 | if ((flags & HeightValue) != 0) |
1026 | 0 | parameters->cp_tdy=(int) geometry.height; |
1027 | 0 | if ((flags & XValue) != 0) |
1028 | 0 | parameters->cp_tx0=(int) geometry.x; |
1029 | 0 | if ((flags & YValue) != 0) |
1030 | 0 | parameters->cp_ty0=(int) geometry.y; |
1031 | 0 | parameters->tile_size_on=OPJ_TRUE; |
1032 | 0 | parameters->numresolution=CalculateNumResolutions((size_t) |
1033 | 0 | parameters->cp_tdx,(size_t) parameters->cp_tdy); |
1034 | 0 | } |
1035 | 8.48k | option=GetImageOption(image_info,"jp2:quality"); |
1036 | 8.48k | if (option != (const char *) NULL) |
1037 | 0 | { |
1038 | 0 | const char |
1039 | 0 | *p; |
1040 | |
|
1041 | 0 | size_t |
1042 | 0 | extent = sizeof(parameters->tcp_distoratio)/ |
1043 | 0 | sizeof(*parameters->tcp_distoratio); |
1044 | | |
1045 | | /* |
1046 | | Set quality PSNR. |
1047 | | */ |
1048 | 0 | p=option; |
1049 | 0 | for (i=0; (i < (ssize_t) (extent-1)) && |
1050 | 0 | (MagickSscanf(p,"%f",¶meters->tcp_distoratio[i]) == 1); i++) |
1051 | 0 | { |
1052 | 0 | while ((*p != '\0') && (*p != ',')) |
1053 | 0 | p++; |
1054 | 0 | if (*p == '\0') |
1055 | 0 | break; |
1056 | 0 | p++; |
1057 | 0 | } |
1058 | 0 | parameters->tcp_numlayers=(int) (i+1); |
1059 | 0 | parameters->cp_fixed_quality=OPJ_TRUE; |
1060 | 0 | parameters->cp_disto_alloc=OPJ_FALSE; |
1061 | 0 | } |
1062 | 8.48k | option=GetImageOption(image_info,"jp2:progression-order"); |
1063 | 8.48k | if (option != (const char *) NULL) |
1064 | 0 | { |
1065 | 0 | if (LocaleCompare(option,"LRCP") == 0) |
1066 | 0 | parameters->prog_order=OPJ_LRCP; |
1067 | 0 | if (LocaleCompare(option,"RLCP") == 0) |
1068 | 0 | parameters->prog_order=OPJ_RLCP; |
1069 | 0 | if (LocaleCompare(option,"RPCL") == 0) |
1070 | 0 | parameters->prog_order=OPJ_RPCL; |
1071 | 0 | if (LocaleCompare(option,"PCRL") == 0) |
1072 | 0 | parameters->prog_order=OPJ_PCRL; |
1073 | 0 | if (LocaleCompare(option,"CPRL") == 0) |
1074 | 0 | parameters->prog_order=OPJ_CPRL; |
1075 | 0 | } |
1076 | 8.48k | option=GetImageOption(image_info,"jp2:rate"); |
1077 | 8.48k | if (option != (const char *) NULL) |
1078 | 0 | { |
1079 | 0 | const char |
1080 | 0 | *p; |
1081 | |
|
1082 | 0 | size_t |
1083 | 0 | extent = sizeof(parameters->tcp_rates)/sizeof(*parameters->tcp_rates); |
1084 | | |
1085 | | /* |
1086 | | Set compression rate. |
1087 | | */ |
1088 | 0 | p=option; |
1089 | 0 | for (i=0; (i < (ssize_t) (extent-1)) && |
1090 | 0 | (MagickSscanf(p,"%f",¶meters->tcp_rates[i]) == 1); i++) |
1091 | 0 | { |
1092 | 0 | while ((*p != '\0') && (*p != ',')) |
1093 | 0 | p++; |
1094 | 0 | if (*p == '\0') |
1095 | 0 | break; |
1096 | 0 | p++; |
1097 | 0 | } |
1098 | 0 | parameters->tcp_numlayers=(int) (i+1); |
1099 | 0 | parameters->cp_disto_alloc=OPJ_TRUE; |
1100 | 0 | } |
1101 | 8.48k | if (image_info->sampling_factor != (char *) NULL) |
1102 | 0 | { |
1103 | 0 | GeometryInfo |
1104 | 0 | geometry_info; |
1105 | |
|
1106 | 0 | MagickStatusType |
1107 | 0 | flags; |
1108 | |
|
1109 | 0 | flags=ParseGeometry(image_info->sampling_factor,&geometry_info); |
1110 | 0 | if ((flags & RhoValue) != 0) |
1111 | 0 | parameters->subsampling_dx=(int) MagickMax( |
1112 | 0 | geometry_info.rho,1.0); |
1113 | 0 | parameters->subsampling_dy=parameters->subsampling_dx; |
1114 | 0 | if ((flags & SigmaValue) != 0) |
1115 | 0 | parameters->subsampling_dy=(int) MagickMax( |
1116 | 0 | geometry_info.sigma,1.0); |
1117 | 0 | } |
1118 | 8.48k | property=GetImageProperty(image,"comment",exception); |
1119 | 8.48k | if (property != (const char *) NULL) |
1120 | 0 | parameters->cp_comment=(char *) property; |
1121 | 8.48k | channels=3; |
1122 | 8.48k | jp2_colorspace=OPJ_CLRSPC_SRGB; |
1123 | 8.48k | if (image->colorspace == YUVColorspace) |
1124 | 0 | jp2_colorspace=OPJ_CLRSPC_SYCC; |
1125 | 8.48k | else |
1126 | 8.48k | { |
1127 | 8.48k | if (IsGrayColorspace(image->colorspace) != MagickFalse) |
1128 | 7.93k | { |
1129 | 7.93k | channels=1; |
1130 | 7.93k | jp2_colorspace=OPJ_CLRSPC_GRAY; |
1131 | 7.93k | } |
1132 | 545 | else |
1133 | 545 | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
1134 | 160 | (void) TransformImageColorspace(image,sRGBColorspace,exception); |
1135 | 8.48k | if (image->alpha_trait != UndefinedPixelTrait) |
1136 | 93 | channels++; |
1137 | 8.48k | } |
1138 | 8.48k | parameters->tcp_mct=channels == 3 ? 1 : 0; |
1139 | 8.48k | memset(jp2_info,0,sizeof(jp2_info)); |
1140 | 18.1k | for (i=0; i < (ssize_t) channels; i++) |
1141 | 9.66k | { |
1142 | 9.66k | jp2_info[i].prec=(OPJ_UINT32) image->depth; |
1143 | 9.66k | if ((image->depth == 1) && |
1144 | 388 | ((LocaleCompare(image_info->magick,"JPT") == 0) || |
1145 | 388 | (LocaleCompare(image_info->magick,"JP2") == 0))) |
1146 | 82 | jp2_info[i].prec++; /* OpenJPEG returns exception for depth @ 1 */ |
1147 | 9.66k | jp2_info[i].sgnd=0; |
1148 | 9.66k | jp2_info[i].dx=(unsigned int) parameters->subsampling_dx; |
1149 | 9.66k | jp2_info[i].dy=(unsigned int) parameters->subsampling_dy; |
1150 | 9.66k | jp2_info[i].w=(OPJ_UINT32) image->columns; |
1151 | 9.66k | jp2_info[i].h=(OPJ_UINT32) image->rows; |
1152 | 9.66k | } |
1153 | 8.48k | jp2_image=opj_image_create((OPJ_UINT32) channels,jp2_info,jp2_colorspace); |
1154 | 8.48k | if (jp2_image == (opj_image_t *) NULL) |
1155 | 0 | { |
1156 | 0 | parameters=(opj_cparameters_t *) RelinquishMagickMemory(parameters); |
1157 | 0 | ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); |
1158 | 0 | } |
1159 | 8.48k | jp2_image->x0=(unsigned int) parameters->image_offset_x0; |
1160 | 8.48k | jp2_image->y0=(unsigned int) parameters->image_offset_y0; |
1161 | 8.48k | jp2_image->x1=(unsigned int) (2*parameters->image_offset_x0+ |
1162 | 8.48k | ((ssize_t) image->columns-1)*parameters->subsampling_dx+1); |
1163 | 8.48k | jp2_image->y1=(unsigned int) (2*parameters->image_offset_y0+ |
1164 | 8.48k | ((ssize_t) image->rows-1)*parameters->subsampling_dy+1); |
1165 | 8.48k | if ((image->depth == 12) && |
1166 | 2.87k | ((image->columns == 2048) || (image->rows == 1080) || |
1167 | 2.05k | (image->columns == 4096) || (image->rows == 2160))) |
1168 | 814 | CinemaProfileCompliance(jp2_image,parameters); |
1169 | 8.48k | if (channels == 4) |
1170 | 57 | jp2_image->comps[3].alpha=1; |
1171 | 8.42k | else |
1172 | 8.42k | if ((channels == 2) && (jp2_colorspace == OPJ_CLRSPC_GRAY)) |
1173 | 36 | jp2_image->comps[1].alpha=1; |
1174 | | /* |
1175 | | Convert to JP2 pixels. |
1176 | | */ |
1177 | 3.55M | for (y=0; y < (ssize_t) image->rows; y++) |
1178 | 3.54M | { |
1179 | 3.54M | const Quantum |
1180 | 3.54M | *p; |
1181 | | |
1182 | 3.54M | ssize_t |
1183 | 3.54M | x; |
1184 | | |
1185 | 3.54M | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1186 | 3.54M | if (p == (const Quantum *) NULL) |
1187 | 0 | break; |
1188 | 3.46G | for (x=0; x < (ssize_t) image->columns; x++) |
1189 | 3.45G | { |
1190 | 6.95G | for (i=0; i < (ssize_t) channels; i++) |
1191 | 3.50G | { |
1192 | 3.50G | double |
1193 | 3.50G | scale; |
1194 | | |
1195 | 3.50G | int |
1196 | 3.50G | *q; |
1197 | | |
1198 | 3.50G | scale=(double) (((size_t) MagickULLConstant(1) << |
1199 | 3.50G | jp2_image->comps[i].prec)-1)/(double) QuantumRange; |
1200 | 3.50G | q=jp2_image->comps[i].data+(ssize_t) (y*MagickSafeReciprocal( |
1201 | 3.50G | jp2_image->comps[i].dy)*image->columns*MagickSafeReciprocal( |
1202 | 3.50G | jp2_image->comps[i].dx)+x*MagickSafeReciprocal( |
1203 | 3.50G | jp2_image->comps[i].dx)); |
1204 | 3.50G | switch (i) |
1205 | 3.50G | { |
1206 | 3.45G | case 0: |
1207 | 3.45G | { |
1208 | 3.45G | if (jp2_colorspace == OPJ_CLRSPC_GRAY) |
1209 | 3.43G | { |
1210 | 3.43G | *q=(int) (scale*(double) GetPixelGray(image,p)); |
1211 | 3.43G | break; |
1212 | 3.43G | } |
1213 | 21.2M | *q=(int) (scale*(double) GetPixelRed(image,p)); |
1214 | 21.2M | break; |
1215 | 3.45G | } |
1216 | 21.2M | case 1: |
1217 | 21.2M | { |
1218 | 21.2M | if (jp2_colorspace == OPJ_CLRSPC_GRAY) |
1219 | 4.34k | { |
1220 | 4.34k | *q=(int) (scale*(double) GetPixelAlpha(image,p)); |
1221 | 4.34k | break; |
1222 | 4.34k | } |
1223 | 21.2M | *q=(int) (scale*(double) GetPixelGreen(image,p)); |
1224 | 21.2M | break; |
1225 | 21.2M | } |
1226 | 21.2M | case 2: |
1227 | 21.2M | { |
1228 | 21.2M | *q=(int) (scale*(double) GetPixelBlue(image,p)); |
1229 | 21.2M | break; |
1230 | 21.2M | } |
1231 | 24.8k | case 3: |
1232 | 24.8k | { |
1233 | 24.8k | *q=(int) (scale*(double) GetPixelAlpha(image,p)); |
1234 | 24.8k | break; |
1235 | 21.2M | } |
1236 | 3.50G | } |
1237 | 3.50G | } |
1238 | 3.45G | p+=(ptrdiff_t) GetPixelChannels(image); |
1239 | 3.45G | } |
1240 | 3.54M | status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, |
1241 | 3.54M | image->rows); |
1242 | 3.54M | if (status == MagickFalse) |
1243 | 0 | break; |
1244 | 3.54M | } |
1245 | 8.48k | if (LocaleCompare(image_info->magick,"JPT") == 0) |
1246 | 0 | jp2_codec=opj_create_compress(OPJ_CODEC_JPT); |
1247 | 8.48k | else |
1248 | 8.48k | if (LocaleCompare(image_info->magick,"J2K") == 0) |
1249 | 2.82k | jp2_codec=opj_create_compress(OPJ_CODEC_J2K); |
1250 | 5.66k | else |
1251 | 5.66k | jp2_codec=opj_create_compress(OPJ_CODEC_JP2); |
1252 | 8.48k | opj_set_warning_handler(jp2_codec,JP2WarningHandler,exception); |
1253 | 8.48k | opj_set_error_handler(jp2_codec,JP2ErrorHandler,exception); |
1254 | 8.48k | opj_setup_encoder(jp2_codec,parameters,jp2_image); |
1255 | 8.48k | jp2_stream=opj_stream_create(OPJ_J2K_STREAM_CHUNK_SIZE,OPJ_FALSE); |
1256 | 8.48k | if (jp2_stream == (opj_stream_t *) NULL) |
1257 | 0 | { |
1258 | 0 | opj_destroy_codec(jp2_codec); |
1259 | 0 | opj_image_destroy(jp2_image); |
1260 | 0 | parameters=(opj_cparameters_t *) RelinquishMagickMemory(parameters); |
1261 | 0 | ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); |
1262 | 0 | } |
1263 | 8.48k | opj_stream_set_read_function(jp2_stream,JP2ReadHandler); |
1264 | 8.48k | opj_stream_set_write_function(jp2_stream,JP2WriteHandler); |
1265 | 8.48k | opj_stream_set_seek_function(jp2_stream,JP2SeekHandler); |
1266 | 8.48k | opj_stream_set_skip_function(jp2_stream,JP2SkipHandler); |
1267 | 8.48k | opj_stream_set_user_data(jp2_stream,image,NULL); |
1268 | 8.48k | jp2_status=opj_start_compress(jp2_codec,jp2_image,jp2_stream); |
1269 | 8.48k | if ((jp2_status == 0) || (opj_encode(jp2_codec,jp2_stream) == 0) || |
1270 | 8.42k | (opj_end_compress(jp2_codec,jp2_stream) == 0)) |
1271 | 61 | { |
1272 | 61 | opj_stream_destroy(jp2_stream); |
1273 | 61 | opj_destroy_codec(jp2_codec); |
1274 | 61 | opj_image_destroy(jp2_image); |
1275 | 61 | parameters=(opj_cparameters_t *) RelinquishMagickMemory(parameters); |
1276 | 61 | ThrowWriterException(DelegateError,"UnableToEncodeImageFile"); |
1277 | 0 | } |
1278 | | /* |
1279 | | Free resources. |
1280 | | */ |
1281 | 8.42k | opj_stream_destroy(jp2_stream); |
1282 | 8.42k | opj_destroy_codec(jp2_codec); |
1283 | 8.42k | opj_image_destroy(jp2_image); |
1284 | 8.42k | parameters=(opj_cparameters_t *) RelinquishMagickMemory(parameters); |
1285 | 8.42k | if (CloseBlob(image) == MagickFalse) |
1286 | 0 | status=MagickFalse; |
1287 | 8.42k | return(status); |
1288 | 8.48k | } |
1289 | | #endif |