/src/graphicsmagick/coders/jp2.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2026 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % |
5 | | % This program is covered by multiple licenses, which are described in |
6 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
7 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
8 | | % |
9 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
10 | | % % |
11 | | % % |
12 | | % % |
13 | | % JJJ PPPP 222 % |
14 | | % J P P 2 2 % |
15 | | % J PPPP 22 % |
16 | | % J J P 2 % |
17 | | % JJ P 22222 % |
18 | | % % |
19 | | % % |
20 | | % Read/Write JPEG-2000 Image Format. % |
21 | | % % |
22 | | % % |
23 | | % John Cristy % |
24 | | % Nathan Brown % |
25 | | % June 2001 % |
26 | | % Bob Friesenhahn % |
27 | | % February 2003 % |
28 | | % % |
29 | | % % |
30 | | % % |
31 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
32 | | % |
33 | | % |
34 | | */ |
35 | | |
36 | | /* |
37 | | Include declarations. |
38 | | */ |
39 | | #include "magick/studio.h" |
40 | | #include "magick/analyze.h" |
41 | | #include "magick/blob.h" |
42 | | #include "magick/pixel_cache.h" |
43 | | #include "magick/log.h" |
44 | | #include "magick/magick.h" |
45 | | #include "magick/monitor.h" |
46 | | #include "magick/profile.h" |
47 | | #include "magick/resource.h" |
48 | | #include "magick/utility.h" |
49 | | #include "magick/static.h" |
50 | | #if defined(HasJP2) |
51 | | # if !defined(uchar) |
52 | | # define uchar unsigned char |
53 | | # endif |
54 | | # if !defined(ushort) |
55 | | # define ushort unsigned short |
56 | | # endif |
57 | | # if !defined(uint) |
58 | | # define uint unsigned int |
59 | | # endif |
60 | | # if !defined(longlong) |
61 | | # define longlong long long |
62 | | # endif |
63 | | # if !defined(ulonglong) |
64 | | # define ulonglong unsigned long long |
65 | | # endif |
66 | | |
67 | | # ifdef __VMS |
68 | | # define JAS_VERSION 1.700.0 |
69 | | # define PACKAGE jasper |
70 | | # define VERSION 1.700.0 |
71 | | # endif /* ifdef __VMS */ |
72 | | # undef PACKAGE_NAME |
73 | | # undef PACKAGE_STRING |
74 | | # undef PACKAGE_TARNAME |
75 | | # undef PACKAGE_VERSION |
76 | | # include "jasper/jasper.h" |
77 | | # undef PACKAGE_NAME |
78 | | # undef PACKAGE_STRING |
79 | | # undef PACKAGE_TARNAME |
80 | | # undef PACKAGE_VERSION |
81 | | /* |
82 | | Old JasPer uses non-persistent '!defined(EXCLUDE_FOO_SUPPORT)' and |
83 | | modern JasPer uses persistent 'if defined(JAS_INCLUDE_FOO_CODEC)' |
84 | | in jas_image.h |
85 | | */ |
86 | | # if defined(EXCLUDE_JP2_SUPPORT) |
87 | | # undef HAVE_JP2_DECODE |
88 | | # endif |
89 | | # if defined(EXCLUDE_JPC_SUPPORT) |
90 | | # undef HAVE_JPC_DECODE |
91 | | # endif |
92 | | # if defined(EXCLUDE_PGX_SUPPORT) |
93 | | # undef HAVE_PGX_DECODE |
94 | | # endif |
95 | | |
96 | | #if defined(HAVE_JAS_INIT_LIBRARY) |
97 | | # define HAVE_JAS_STREAM_IO_V3 |
98 | | #endif |
99 | | |
100 | | #if 0 |
101 | | /* Development JasPer 3.0.0 jas_init_library() is not yet ready for our purposes */ |
102 | | #if !(defined(MAGICK_ENABLE_JAS_INIT_LIBRARY) && MAGICK_ENABLE_JAS_INIT_LIBRARY) |
103 | | #undef HAVE_JAS_INIT_LIBRARY |
104 | | #endif /* if !(defined(MAGICK_ENABLE_JAS_INIT_LIBRARY) && MAGICK_ENABLE_JAS_INIT_LIBRARY) */ |
105 | | #endif |
106 | | |
107 | | #if defined(HAVE_PTHREAD) || defined(MSWINDOWS) || defined(HAVE_OPENMP) |
108 | 10 | # define JP2_HAVE_THREADS 1 |
109 | | #else |
110 | | # define JP2_HAVE_THREADS 0 |
111 | | #endif |
112 | | |
113 | | |
114 | | /* |
115 | | Forward declarations. |
116 | | */ |
117 | | static unsigned int |
118 | | WriteJP2Image(const ImageInfo *,Image *); |
119 | | |
120 | | static MagickBool jasper_initialized=MagickFalse; |
121 | | static const char jasper_enc_options[][11] = |
122 | | { |
123 | | "cblkheight", |
124 | | "cblkwidth", |
125 | | "debug", |
126 | | "eph", |
127 | | "ilyrrates", |
128 | | "imgareatlx", |
129 | | "imgareatly", |
130 | | "lazy", |
131 | | "mode", |
132 | | "nomct", |
133 | | "numgbits", |
134 | | "numrlvls", |
135 | | "prcheight", |
136 | | "prcwidth", |
137 | | "prg", |
138 | | "pterm", |
139 | | "rate", |
140 | | "resetprob", |
141 | | "segsym", |
142 | | "sop", |
143 | | "termall", |
144 | | "tilegrdtlx", |
145 | | "tilegrdtly", |
146 | | "tileheight", |
147 | | "tilewidth", |
148 | | "vcausal" |
149 | | }; |
150 | | |
151 | | static const char jasper_dec_options[][12] = |
152 | | { |
153 | | "allow_trunc", |
154 | | "debug", |
155 | | "max_samples", |
156 | | "maxlyrs", |
157 | | "maxpkts", |
158 | | "version" |
159 | | }; |
160 | | |
161 | | |
162 | | /* |
163 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
164 | | % % |
165 | | % % |
166 | | % % |
167 | | % I s J P 2 % |
168 | | % % |
169 | | % % |
170 | | % % |
171 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
172 | | % |
173 | | % Method IsJP2 returns True if the image format type, identified by the |
174 | | % magick string, is JP2. |
175 | | % |
176 | | % The format of the IsJP2 method is: |
177 | | % |
178 | | % unsigned int IsJP2(const unsigned char *magick,const size_t length) |
179 | | % |
180 | | % A description of each parameter follows: |
181 | | % |
182 | | % o status: Method IsJP2 returns True if the image format type is JP2. |
183 | | % |
184 | | % o magick: This string is generally the first few bytes of an image file |
185 | | % or blob. |
186 | | % |
187 | | % o length: Specifies the length of the magick string. |
188 | | % |
189 | | % |
190 | | */ |
191 | | static unsigned int IsJP2(const unsigned char *magick,const size_t length) |
192 | 422k | { |
193 | 422k | if (length < 9) |
194 | 0 | return(False); |
195 | 422k | if (memcmp(magick+4,"\152\120\040\040\015",5) == 0) |
196 | 18.8k | return(True); |
197 | 403k | return(False); |
198 | 422k | } |
199 | | |
200 | | /* |
201 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
202 | | % % |
203 | | % % |
204 | | % % |
205 | | % I s J P C % |
206 | | % % |
207 | | % % |
208 | | % % |
209 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
210 | | % |
211 | | % Method IsJPC returns True if the image format type, identified by the |
212 | | % magick string, is JPC. |
213 | | % |
214 | | % The format of the IsJPC method is: |
215 | | % |
216 | | % unsigned int IsJPC(const unsigned char *magick,const size_t length) |
217 | | % |
218 | | % A description of each parameter follows: |
219 | | % |
220 | | % o status: Method IsJPC returns True if the image format type is JPC. |
221 | | % |
222 | | % o magick: This string is generally the first few bytes of an image file |
223 | | % or blob. |
224 | | % |
225 | | % o length: Specifies the length of the magick string. |
226 | | % |
227 | | % |
228 | | */ |
229 | | static unsigned int IsJPC(const unsigned char *magick,const size_t length) |
230 | 422k | { |
231 | 422k | if (length < 2) |
232 | 0 | return(False); |
233 | 422k | if (memcmp(magick,"\377\117",2) == 0) |
234 | 201k | return(True); |
235 | 220k | return(False); |
236 | 422k | } |
237 | | |
238 | | /* |
239 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
240 | | % % |
241 | | % % |
242 | | % % |
243 | | % I s P G X % |
244 | | % % |
245 | | % % |
246 | | % % |
247 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
248 | | % |
249 | | % Method IsPGX returns True if the image format type, identified by the |
250 | | % magick string, is PGX. PGX is an uncompressed raster image file format |
251 | | % used in JPEG 2000 conformance testing. PGX file stores only a single |
252 | | % component, so it is limited to grayscale. |
253 | | % |
254 | | % The format of the IsPGX method is: |
255 | | % |
256 | | % unsigned int IsPGX(const unsigned char *magick,const size_t length) |
257 | | % |
258 | | % A description of each parameter follows: |
259 | | % |
260 | | % o status: Method IsPGX returns True if the image format type is PGX. |
261 | | % |
262 | | % o magick: This string is generally the first few bytes of an image file |
263 | | % or blob. |
264 | | % |
265 | | % o length: Specifies the length of the magick string. |
266 | | % |
267 | | % |
268 | | */ |
269 | | static unsigned int IsPGX(const unsigned char *magick,const size_t length) |
270 | 422k | { |
271 | 422k | if (length < 5) |
272 | 0 | return(False); |
273 | 422k | if ((memcmp(magick,"PG ML",5) == 0) || (memcmp(magick,"PG LM",5) == 0)) |
274 | 204k | return(True); |
275 | 218k | return(False); |
276 | 422k | } |
277 | | |
278 | | /* |
279 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
280 | | % % |
281 | | % % |
282 | | % % |
283 | | % R e a d J P 2 I m a g e % |
284 | | % % |
285 | | % % |
286 | | % % |
287 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
288 | | % |
289 | | % Method ReadJP2Image reads a JPEG 2000 Image file (JP2) or JPEG 2000 |
290 | | % codestream (JPC) image file and returns it. It allocates the memory |
291 | | % necessary for the new Image structure and returns a pointer to the new |
292 | | % image or set of images. |
293 | | % |
294 | | % JP2 support is originally written by Nathan Brown, nathanbrown@letu.edu. |
295 | | % |
296 | | % The format of the ReadJP2Image method is: |
297 | | % |
298 | | % Image *ReadJP2Image(const ImageInfo *image_info, |
299 | | % ExceptionInfo *exception) |
300 | | % |
301 | | % A description of each parameter follows: |
302 | | % |
303 | | % o image: Method ReadJP2Image returns a pointer to the image after |
304 | | % reading. A null image is returned if there is a memory shortage or |
305 | | % if the image cannot be read. |
306 | | % |
307 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
308 | | % |
309 | | % o exception: return any errors or warnings in this structure. |
310 | | % |
311 | | */ |
312 | | |
313 | | typedef struct _StreamManager |
314 | | { |
315 | | jas_stream_t |
316 | | *stream; |
317 | | |
318 | | Image |
319 | | *image; |
320 | | } StreamManager; |
321 | | |
322 | | /* |
323 | | I/O read/write callbacks changed |
324 | | |
325 | | Cnt argument changed from 'int' to 'unsigned' on 6/29/20 (2.0.19). |
326 | | |
327 | | Write write buf pointer changed from 'char *' to 'const char *' on 8/14/20 |
328 | | |
329 | | Old interface: |
330 | | int (*read_)(jas_stream_obj_t *obj, char *buf, int cnt); |
331 | | int (*write_)(jas_stream_obj_t *obj, char *buf, int cnt); |
332 | | |
333 | | New interface: |
334 | | int (*read_)(jas_stream_obj_t *obj, char *buf, unsigned cnt); |
335 | | int (*write_)(jas_stream_obj_t *obj, const char *buf, unsigned cnt); |
336 | | |
337 | | In Jasper 3.0.0 the interface changed again: |
338 | | ssize_t (*read_)(jas_stream_obj_t *obj, char *buf, size_t cnt); |
339 | | ssize_t (*write_)(jas_stream_obj_t *obj, const char *buf, size_t cnt); |
340 | | |
341 | | We have yet to find a useful way to determine the version of the |
342 | | JasPer library using the C pre-processor. |
343 | | */ |
344 | | |
345 | | /* Read characters from a file object. */ |
346 | | /* ssize_t (*read_)(jas_stream_obj_t *obj, char *buf, size_t cnt); */ |
347 | | #if defined(HAVE_JAS_STREAM_IO_V3) |
348 | | static ssize_t BlobRead(jas_stream_obj_t *obj, char *buf, size_t cnt) |
349 | | #else |
350 | | static int BlobRead(jas_stream_obj_t *obj, char *buf, unsigned cnt) |
351 | | #endif |
352 | 54.9M | { |
353 | 54.9M | size_t |
354 | 54.9M | count; |
355 | | |
356 | 54.9M | StreamManager |
357 | 54.9M | *source = (StreamManager *) obj; |
358 | | |
359 | 54.9M | count=ReadBlob(source->image,(size_t) cnt,(void *) buf); |
360 | 54.9M | #if defined(HAVE_JAS_STREAM_IO_V3) |
361 | 54.9M | return (count); |
362 | | #else |
363 | | if ((size_t)((int) count) != count) |
364 | | count = 0; |
365 | | return ((int) count); |
366 | | #endif |
367 | 54.9M | } |
368 | | |
369 | | /* Write characters to a file object. */ |
370 | | /* ssize_t (*write_)(jas_stream_obj_t *obj, const char *buf, size_t cnt); */ |
371 | | #if defined(HAVE_JAS_STREAM_IO_V3) |
372 | | static ssize_t BlobWrite(jas_stream_obj_t *obj, const char *buf, size_t cnt) |
373 | | #else |
374 | | static int BlobWrite(jas_stream_obj_t *obj, const char *buf, unsigned cnt) |
375 | | #endif |
376 | 4.44M | { |
377 | 4.44M | size_t |
378 | 4.44M | count; |
379 | | |
380 | 4.44M | StreamManager |
381 | 4.44M | *source = (StreamManager *) obj; |
382 | | |
383 | 4.44M | count=WriteBlob(source->image,(size_t) cnt,(void *) buf); |
384 | 4.44M | #if defined(HAVE_JAS_STREAM_IO_V3) |
385 | 4.44M | return(count); |
386 | | #else |
387 | | if ((size_t)((int) count) != count) |
388 | | count = 0; |
389 | | return ((int) count); |
390 | | #endif |
391 | 4.44M | } |
392 | | |
393 | | /* Set the position for a file object. */ |
394 | | /* long (*seek_)(jas_stream_obj_t *obj, long offset, int origin); */ |
395 | | static long BlobSeek(jas_stream_obj_t *obj,long offset,int origin) |
396 | 0 | { |
397 | 0 | StreamManager |
398 | 0 | *source = (StreamManager *) obj; |
399 | |
|
400 | 0 | return (SeekBlob(source->image,offset,origin)); |
401 | 0 | } |
402 | | |
403 | | /* Close a file object. */ |
404 | | /* int (*close_)(jas_stream_obj_t *obj); */ |
405 | | static int BlobClose(jas_stream_obj_t *obj) |
406 | 424k | { |
407 | 424k | int status; |
408 | 424k | StreamManager |
409 | 424k | *source = (StreamManager *) obj; |
410 | | |
411 | 424k | status = CloseBlob(source->image); |
412 | 424k | jas_free(source); |
413 | 424k | return (status == 0 ? EOF : 0); |
414 | 424k | } |
415 | | |
416 | | |
417 | | static jas_stream_t *JP2StreamManager(jas_stream_ops_t *stream_ops, Image *image) |
418 | 424k | { |
419 | 424k | jas_stream_t |
420 | 424k | *stream; |
421 | | |
422 | 424k | StreamManager |
423 | 424k | *source; |
424 | | |
425 | 424k | stream=(jas_stream_t *) jas_malloc(sizeof(jas_stream_t)); |
426 | 424k | if (stream == (jas_stream_t *) NULL) |
427 | 0 | return((jas_stream_t *) NULL); |
428 | 424k | (void) memset(stream,0,sizeof(jas_stream_t)); |
429 | 424k | stream->rwlimit_=(-1); |
430 | 424k | stream->obj_=(jas_stream_obj_t *) jas_malloc(sizeof(StreamManager)); |
431 | 424k | if (stream->obj_ == (jas_stream_obj_t *) NULL) |
432 | 0 | { |
433 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
434 | 0 | "jas_malloc() failed!"); |
435 | 0 | jas_free(stream); |
436 | 0 | return((jas_stream_t *) NULL); |
437 | 0 | } |
438 | 424k | stream->ops_=stream_ops; |
439 | 424k | stream->openmode_=JAS_STREAM_READ | JAS_STREAM_WRITE | JAS_STREAM_BINARY; |
440 | 424k | stream->bufbase_=stream->tinybuf_; |
441 | 424k | stream->bufsize_=1; |
442 | 424k | stream->bufstart_=(&stream->bufbase_[JAS_STREAM_MAXPUTBACK]); |
443 | 424k | stream->ptr_=stream->bufstart_; |
444 | 424k | stream->bufmode_|=JAS_STREAM_UNBUF & JAS_STREAM_BUFMODEMASK; |
445 | 424k | source=(StreamManager *) stream->obj_; |
446 | 424k | source->image=image; |
447 | 424k | return(stream); |
448 | 424k | } |
449 | | |
450 | | #if defined(HAVE_JAS_INIT_LIBRARY) |
451 | 429k | # define JAS_CLEANUP_THREAD() jas_cleanup_thread() |
452 | | #else |
453 | | # define JAS_CLEANUP_THREAD() |
454 | | #endif |
455 | | |
456 | | #define ThrowJP2ReaderException(code_,reason_,image_) \ |
457 | 395k | { \ |
458 | 408k | for (component=0; component < (long) number_components; component++) \ |
459 | 395k | MagickFreeResourceLimitedMemory(Quantum *,channel_lut[component]); \ |
460 | 395k | if (pixels) \ |
461 | 395k | jas_matrix_destroy(pixels); \ |
462 | 395k | if (jp2_stream) \ |
463 | 395k | (void) jas_stream_close(jp2_stream); \ |
464 | 395k | if (jp2_image) \ |
465 | 395k | jas_image_destroy(jp2_image); \ |
466 | 395k | MagickFreeMemory(options); \ |
467 | 395k | JAS_CLEANUP_THREAD(); \ |
468 | 395k | ThrowReaderException(code_,reason_,image_); \ |
469 | 0 | } |
470 | | |
471 | | #define ThrowJP2WriterException(code_,reason_,image_) \ |
472 | 8 | { \ |
473 | 8 | JAS_CLEANUP_THREAD(); \ |
474 | 8 | ThrowWriterException(code_,reason_,image_); \ |
475 | 0 | } |
476 | | |
477 | | /* |
478 | | Initialize Jasper |
479 | | */ |
480 | | #if HAVE_JAS_INIT_LIBRARY |
481 | | static void *alloc_rlm(struct jas_allocator_s *allocator, size_t size) |
482 | 403M | { |
483 | 403M | char *p; |
484 | 403M | (void) allocator; |
485 | | /* JasPer expects its allocator to return non-null for zero size */ |
486 | 403M | p=(char *) _MagickAllocateResourceLimitedMemoryLoc((size == 0 ? 1 : size),GetMagickModule()); |
487 | | /* fprintf(stderr,"alloc_rlm(%p, %zu) -> %p\n", allocator, size, p); */ |
488 | 403M | return p; |
489 | 403M | } |
490 | | static void free_rlm(struct jas_allocator_s *allocator, void *pointer) |
491 | 403M | { |
492 | 403M | (void) allocator; |
493 | | /* fprintf(stderr,"free_rlm(%p, %p\n", allocator, pointer); */ |
494 | 403M | _MagickFreeResourceLimitedMemoryLoc(pointer,GetMagickModule()); |
495 | 403M | } |
496 | | static void *realloc_rlm(struct jas_allocator_s *allocator, void *pointer, |
497 | | size_t new_size) |
498 | 96.9k | { |
499 | 96.9k | char *p; |
500 | 96.9k | (void) allocator; |
501 | 96.9k | p =(char *) _MagickReallocateResourceLimitedMemoryLoc(pointer, 1,new_size,0,GetMagickModule()); |
502 | | /* fprintf(stderr,"realloc_rlm(%p, %p, %zu) -> %p\n", allocator, pointer, new_size, p); */ |
503 | 96.9k | return p; |
504 | 96.9k | } |
505 | | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
506 | | static MagickPassFail initialize_jasper(ExceptionInfo *exception) |
507 | 429k | { |
508 | 429k | (void) exception; |
509 | 429k | if (!jasper_initialized) |
510 | 10 | { |
511 | 10 | #if HAVE_JAS_INIT_LIBRARY |
512 | 10 | { |
513 | | /* static jas_std_allocator_t allocator; */ |
514 | 10 | static jas_allocator_t allocator; |
515 | | |
516 | 10 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
517 | 10 | "Initializing JasPer..."); |
518 | | /* |
519 | | Configure the library using the default configuration settings. |
520 | | */ |
521 | 10 | jas_conf_clear(); |
522 | | |
523 | | /* |
524 | | Provide our own resource-limited memory allocation |
525 | | functions. |
526 | | |
527 | | See src/libjasper/include/jasper/jas_malloc.h |
528 | | */ |
529 | | |
530 | | /* |
531 | | Function to clean up the allocator when no longer needed. |
532 | | The allocator cannot be used after the clean-up operation is performed. |
533 | | This function pointer may be null, in which case the clean-up operation |
534 | | is treated as a no-op. |
535 | | */ |
536 | 10 | allocator.cleanup = 0; |
537 | | |
538 | | /* |
539 | | Function to allocate memory. |
540 | | This function should have behavior similar to malloc. |
541 | | */ |
542 | 10 | allocator.alloc = alloc_rlm; |
543 | | |
544 | | /* |
545 | | Function to deallocate memory. |
546 | | This function should have behavior similar to free. |
547 | | */ |
548 | 10 | allocator.free = free_rlm; |
549 | | |
550 | | /* |
551 | | Function to reallocate memory. |
552 | | This function should have behavior similar to realloc. |
553 | | */ |
554 | 10 | allocator.realloc = realloc_rlm; |
555 | | /* jas_std_allocator_init(&allocator); */ /* Uses JasPer allocators */ |
556 | 10 | jas_conf_set_allocator(&allocator); /* Assigns jas_allocator_t to jas_conf.allocator in library */ |
557 | | /* jas_conf_set_debug_level(cmdopts->debug); */ |
558 | | |
559 | | /* |
560 | | Tell JasPer how much memory it could ever be allowed to use. |
561 | | */ |
562 | 10 | { |
563 | 10 | size_t max_mem_gm = (size_t) GetMagickResourceLimit(MemoryResource); |
564 | 10 | size_t max_mem_jas = jas_get_total_mem_size(); |
565 | 10 | if (max_mem_jas == 0) |
566 | 0 | max_mem_jas=max_mem_gm; |
567 | 10 | jas_conf_set_max_mem_usage(Min(max_mem_jas,max_mem_gm)); |
568 | 10 | } |
569 | | |
570 | | /* |
571 | | Inform JasPer that app may be multi-threaded |
572 | | */ |
573 | 10 | jas_conf_set_multithread(JP2_HAVE_THREADS); |
574 | | |
575 | | /* Perform global initialization for the JasPer library. */ |
576 | 10 | if (jas_init_library() == 0) |
577 | 10 | { |
578 | 10 | jasper_initialized=MagickTrue; |
579 | | /* jas_set_debug_level(110); */ |
580 | 10 | } |
581 | 0 | else |
582 | 0 | { |
583 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
584 | 0 | "jas_init_library() failed!"); |
585 | 0 | } |
586 | 10 | } |
587 | | #else |
588 | | { |
589 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
590 | | "Initializing JasPer..."); |
591 | | if (jas_init() == 0) |
592 | | { |
593 | | jasper_initialized=MagickTrue; |
594 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
595 | | "Initialized JasPer"); |
596 | | } |
597 | | else |
598 | | { |
599 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
600 | | "jas_init() failed!"); |
601 | | } |
602 | | } |
603 | | #endif /* HAVE_JAS_INIT_LIBRARY */ |
604 | | |
605 | 10 | if (!jasper_initialized) |
606 | 0 | { |
607 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
608 | 0 | "Failed to initialize JasPer!"); |
609 | 0 | } |
610 | 10 | } |
611 | | |
612 | 429k | return jasper_initialized ? MagickPass : MagickFail; |
613 | 429k | } |
614 | | |
615 | | |
616 | | /* |
617 | | Cleanup Jasper |
618 | | */ |
619 | | static void cleanup_jasper(void) |
620 | 0 | { |
621 | 0 | if (jasper_initialized) |
622 | 0 | { |
623 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
624 | 0 | "Destroying JasPer..."); |
625 | 0 | #if HAVE_JAS_INIT_LIBRARY |
626 | | /* Perform global cleanup for the JasPer library. */ |
627 | 0 | jas_cleanup_library(); |
628 | | #else |
629 | | jas_cleanup(); |
630 | | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
631 | 0 | jasper_initialized=MagickFalse; |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | | static Image *ReadJP2Image(const ImageInfo *image_info, |
636 | | ExceptionInfo *exception) |
637 | 427k | { |
638 | 427k | Image |
639 | 427k | *image; |
640 | | |
641 | 427k | long |
642 | 427k | y; |
643 | | |
644 | 427k | jas_image_t |
645 | 427k | *jp2_image = (jas_image_t *) NULL; |
646 | | |
647 | 427k | jas_matrix_t |
648 | 427k | *pixels = (jas_matrix_t *) NULL; |
649 | | |
650 | 427k | jas_stream_ops_t |
651 | 427k | StreamOperators = |
652 | 427k | { |
653 | 427k | BlobRead, |
654 | 427k | BlobWrite, |
655 | 427k | BlobSeek, |
656 | 427k | BlobClose |
657 | 427k | }; |
658 | | |
659 | 427k | jas_stream_t |
660 | 427k | *jp2_stream = (jas_stream_t *) NULL; |
661 | | |
662 | 427k | register long |
663 | 427k | x; |
664 | | |
665 | 427k | register PixelPacket |
666 | 427k | *q; |
667 | | |
668 | 427k | magick_off_t |
669 | 427k | pos; |
670 | | |
671 | 427k | int |
672 | 427k | component, |
673 | 427k | components[4], |
674 | 427k | number_components=0; |
675 | | |
676 | 427k | Quantum |
677 | 427k | *channel_lut[4]; |
678 | | |
679 | 427k | char |
680 | 427k | option_keyval[MaxTextExtent], |
681 | 427k | *options = NULL; |
682 | | |
683 | 427k | unsigned int |
684 | 427k | status; |
685 | | |
686 | 427k | MagickBool |
687 | 427k | jp2_hdr, |
688 | 427k | jpc_hdr, |
689 | 427k | pgx_hdr; |
690 | | |
691 | | /* |
692 | | Initialize Jasper |
693 | | */ |
694 | 427k | if (initialize_jasper(exception) != MagickPass) |
695 | 0 | { |
696 | 0 | return (Image *) NULL; |
697 | 0 | } |
698 | | |
699 | 427k | #if HAVE_JAS_INIT_LIBRARY |
700 | | /* |
701 | | Perform any per-thread initialization for the JasPer library. |
702 | | */ |
703 | 427k | if (jas_init_thread()) |
704 | 0 | { |
705 | | /* Handle the initialization error. */ |
706 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
707 | 0 | "jas_init_thread() failed!"); |
708 | 0 | return (Image *) NULL; |
709 | 0 | } |
710 | 427k | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
711 | | |
712 | | /* |
713 | | Open image file. |
714 | | */ |
715 | 427k | assert(image_info != (const ImageInfo *) NULL); |
716 | 427k | assert(image_info->signature == MagickSignature); |
717 | 427k | assert(exception != (ExceptionInfo *) NULL); |
718 | 427k | assert(exception->signature == MagickSignature); |
719 | 427k | (void) memset(channel_lut,0,sizeof(channel_lut)); |
720 | 427k | image=AllocateImage(image_info); |
721 | 427k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
722 | 427k | if (status == False) |
723 | 427k | ThrowJP2ReaderException(FileOpenError,UnableToOpenFile,image); |
724 | | |
725 | 427k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
726 | 427k | "Requested format is \"%s\"", |
727 | 427k | image_info->magick); |
728 | | |
729 | | /* |
730 | | Get the header and auto-detect apparent format. |
731 | | */ |
732 | 427k | { |
733 | 427k | size_t |
734 | 427k | magick_length; |
735 | | |
736 | 427k | unsigned char |
737 | 427k | magick[16]; |
738 | | |
739 | 427k | const MagickInfo |
740 | 427k | *magick_info; |
741 | | |
742 | | /* Get current seek position (normally 0) */ |
743 | 427k | pos=TellBlob(image); |
744 | 427k | if (pos < 0) |
745 | 0 | { |
746 | 0 | ThrowJP2ReaderException(BlobError,UnableToObtainOffset,image); |
747 | 0 | } |
748 | | |
749 | | /* Read header */ |
750 | 427k | if ((magick_length=ReadBlob(image,sizeof(magick),magick)) != sizeof(magick)) |
751 | 4.96k | { |
752 | 4.96k | ThrowJP2ReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
753 | 0 | } |
754 | | |
755 | | /* Restore seek position */ |
756 | 422k | if (SeekBlob(image,pos,SEEK_SET) != pos) |
757 | 0 | { |
758 | 0 | ThrowJP2ReaderException(BlobError,UnableToSeekToOffset,image); |
759 | 0 | } |
760 | | |
761 | | /* Inspect header to see what it might actually be */ |
762 | 422k | jp2_hdr=IsJP2(magick,sizeof(magick)); |
763 | 422k | jpc_hdr=IsJPC(magick,sizeof(magick)); |
764 | 422k | pgx_hdr=IsPGX(magick,sizeof(magick)); |
765 | | |
766 | | /* |
767 | | If input format was previously auto-detected or specified, then |
768 | | assure that header matches what was specified. |
769 | | */ |
770 | 422k | if (((LocaleCompare(image_info->magick,"JP2") == 0) && !jp2_hdr) || |
771 | 422k | (((LocaleCompare(image_info->magick,"JPC") == 0) || |
772 | 225k | (LocaleCompare(image_info->magick,"J2C") == 0)) && !jpc_hdr) || |
773 | 422k | ((LocaleCompare(image_info->magick,"PGX") == 0) && !pgx_hdr)) |
774 | 229 | { |
775 | 229 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
776 | 229 | "Not a \"%s\" file!", image_info->magick); |
777 | 229 | ThrowJP2ReaderException(CorruptImageError,ImproperImageHeader,image); |
778 | 0 | } |
779 | | |
780 | | /* |
781 | | Throw exception if header is not one we expect. |
782 | | */ |
783 | 422k | if (!jp2_hdr && !jpc_hdr && !pgx_hdr) |
784 | 0 | { |
785 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
786 | 0 | "Header is not a supported type for this coder"); |
787 | 0 | ThrowJP2ReaderException(CorruptImageError,ImproperImageHeader,image); |
788 | 0 | } |
789 | | |
790 | | /* |
791 | | Check if we are allowed to decode this format. |
792 | | */ |
793 | 422k | if (((magick_info = GetMagickInfo(image_info->magick,exception)) == |
794 | 422k | (const MagickInfo *) NULL) || |
795 | 422k | (magick_info->decoder == (DecoderHandler) NULL)) |
796 | 0 | { |
797 | 0 | ThrowJP2ReaderException(DelegateError,UnableToDecodeImageFile,image); |
798 | 0 | } |
799 | 422k | } |
800 | | |
801 | | /* |
802 | | Obtain a JP2 Stream. |
803 | | */ |
804 | 422k | jp2_stream=JP2StreamManager(&StreamOperators, image); |
805 | 422k | if (jp2_stream == (jas_stream_t *) NULL) |
806 | 422k | ThrowJP2ReaderException(DelegateError,UnableToManageJP2Stream,image); |
807 | | |
808 | | /* |
809 | | Support passing Jasper options. |
810 | | */ |
811 | 422k | { |
812 | 422k | unsigned int |
813 | 422k | i; |
814 | | |
815 | 422k | MagickBool |
816 | 422k | max_samples_specified = MagickFalse; |
817 | | |
818 | 2.95M | for (i=0; i < ArraySize(jasper_dec_options); i++) |
819 | 2.53M | { |
820 | 2.53M | const char |
821 | 2.53M | *option = jasper_dec_options[i]; |
822 | | |
823 | 2.53M | const char |
824 | 2.53M | *value; |
825 | | |
826 | 2.53M | if ((value=AccessDefinition(image_info,"jp2",option)) != NULL) |
827 | 0 | { |
828 | 0 | MagickFormatString(option_keyval,sizeof(option_keyval),"%s=%.1024s ",option,value); |
829 | 0 | ConcatenateString(&options,option_keyval); |
830 | |
|
831 | 0 | if (LocaleCompare(option,"max_samples") == 0) |
832 | 0 | max_samples_specified=MagickTrue; |
833 | | |
834 | | /* Setting debug mode seems to require extra assistance */ |
835 | 0 | if (LocaleCompare(option,"debug") == 0) |
836 | 0 | jas_setdbglevel(atoi(value)); |
837 | 0 | } |
838 | 2.53M | } |
839 | | |
840 | | /* |
841 | | If max_samples argument was not specified, then pass options |
842 | | argument which specifies "max_samples" to cap memory usage. |
843 | | */ |
844 | 422k | if (!max_samples_specified) |
845 | 422k | { |
846 | 422k | const magick_uintmax_t width_limit = (magick_uintmax_t) GetMagickResourceLimit(WidthResource); |
847 | 422k | const magick_uintmax_t height_limit = (magick_uintmax_t) GetMagickResourceLimit(HeightResource); |
848 | 422k | const magick_uintmax_t max_samples_rectangle = width_limit*height_limit; |
849 | 422k | const magick_uintmax_t max_samples_pixels = (magick_uintmax_t) GetMagickResourceLimit(PixelsResource); |
850 | 422k | const magick_uintmax_t max_samples = Min(max_samples_rectangle,max_samples_pixels); |
851 | | |
852 | 422k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
853 | 422k | "max_samples limits: max_samples_rectangle = " |
854 | 422k | "%" MAGICK_UINTMAX_F "u, max_samples_pixels = %" MAGICK_UINTMAX_F "u", |
855 | 422k | max_samples_rectangle, max_samples_pixels); |
856 | | |
857 | 422k | MagickFormatString(option_keyval,sizeof(option_keyval),"max_samples=%" MAGICK_UINTMAX_F "u ", max_samples*3); |
858 | 422k | ConcatenateString(&options,option_keyval); |
859 | 422k | } |
860 | | |
861 | 422k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
862 | 422k | "JP2 options = \"%s\"", options); |
863 | 422k | } |
864 | | |
865 | | /* |
866 | | Decode |
867 | | */ |
868 | 422k | { |
869 | 422k | int |
870 | 422k | jas_fmt = -1; |
871 | | |
872 | 422k | const char * |
873 | 422k | jas_fmt_str = NULL; |
874 | | |
875 | 422k | if (jp2_hdr) |
876 | 18.8k | jas_fmt_str = "jp2"; |
877 | 403k | else if (jpc_hdr) |
878 | 199k | jas_fmt_str = "jpc"; |
879 | 204k | else if (pgx_hdr) |
880 | 204k | jas_fmt_str = "pgx"; |
881 | | |
882 | 422k | if (jas_fmt_str != (const char *) NULL) |
883 | 422k | { |
884 | 422k | jas_fmt = jas_image_strtofmt(jas_fmt_str); |
885 | 422k | if (-1 != jas_fmt) |
886 | 422k | { |
887 | 422k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
888 | 422k | "Decoding %s...", jas_fmt_str); |
889 | 422k | jp2_image=jas_image_decode(jp2_stream,jas_fmt,options); |
890 | 422k | } |
891 | 0 | else |
892 | 0 | { |
893 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
894 | 0 | "JasPer does not support format %s!", |
895 | 0 | jas_fmt_str); |
896 | 0 | } |
897 | 422k | } |
898 | 422k | } |
899 | | |
900 | 422k | MagickFreeMemory(options); |
901 | | |
902 | 422k | if (jp2_image == (jas_image_t *) NULL) |
903 | 377k | ThrowJP2ReaderException(DelegateError,UnableToDecodeImageFile,image); |
904 | | |
905 | | /* |
906 | | Validate that we can handle the image and obtain component |
907 | | indexes. |
908 | | */ |
909 | 45.0k | switch (jas_clrspc_fam(jas_image_clrspc(jp2_image))) |
910 | 45.0k | { |
911 | 448 | case JAS_CLRSPC_FAM_RGB: |
912 | 448 | { |
913 | 448 | if (((components[0]= |
914 | 448 | jas_image_getcmptbytype(jp2_image, |
915 | 448 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R))) < 0) || |
916 | 443 | ((components[1]= |
917 | 443 | jas_image_getcmptbytype(jp2_image, |
918 | 443 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G))) < 0) || |
919 | 439 | ((components[2]= |
920 | 439 | jas_image_getcmptbytype(jp2_image, |
921 | 439 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B))) < 0)) |
922 | 10 | { |
923 | 10 | ThrowJP2ReaderException(CorruptImageError,MissingImageChannel,image); |
924 | 0 | } |
925 | 438 | number_components=3; |
926 | 438 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
927 | 438 | "Image is in RGB colorspace family"); |
928 | 438 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
929 | 438 | "RED is in channel %d, GREEN is in channel %d, BLUE is in channel %d", |
930 | 438 | components[0],components[1],components[2]); |
931 | | |
932 | 438 | if((components[3]=jas_image_getcmptbytype(jp2_image, |
933 | 438 | JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY))) > 0) |
934 | 22 | { |
935 | 22 | image->matte=MagickTrue; |
936 | 22 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
937 | 22 | "OPACITY is in channel %d",components[3]); |
938 | 22 | number_components++; |
939 | 22 | } |
940 | 438 | break; |
941 | 448 | } |
942 | 44.4k | case JAS_CLRSPC_FAM_GRAY: |
943 | 44.4k | { |
944 | 44.4k | if ((components[0]= |
945 | 44.4k | jas_image_getcmptbytype(jp2_image, |
946 | 44.4k | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y))) < 0) |
947 | 44.4k | ThrowJP2ReaderException(CorruptImageError,MissingImageChannel,image); |
948 | 44.4k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
949 | 44.4k | "Image is in GRAY colorspace family"); |
950 | 44.4k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
951 | 44.4k | "GRAY is in channel %d",components[0]); |
952 | 44.4k | number_components=1; |
953 | 44.4k | break; |
954 | 44.4k | } |
955 | 83 | case JAS_CLRSPC_FAM_YCBCR: |
956 | 83 | { |
957 | 83 | components[0]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_Y); |
958 | 83 | components[1]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CB); |
959 | 83 | components[2]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_YCBCR_CR); |
960 | 83 | if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0)) |
961 | 80 | ThrowJP2ReaderException(CorruptImageError,MissingImageChannel,image); |
962 | 80 | number_components=3; |
963 | 80 | components[3]=jas_image_getcmptbytype(jp2_image,JAS_IMAGE_CT_OPACITY); |
964 | 80 | if (components[3] > 0) |
965 | 0 | { |
966 | 0 | image->matte=True; |
967 | 0 | number_components++; |
968 | 0 | } |
969 | 80 | image->colorspace=YCbCrColorspace; |
970 | 80 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
971 | 80 | "Image is in YCBCR colorspace family"); |
972 | 80 | break; |
973 | 83 | } |
974 | 72 | default: |
975 | 72 | { |
976 | 72 | ThrowJP2ReaderException(CoderError,ColorspaceModelIsNotSupported,image); |
977 | 0 | } |
978 | 45.0k | } |
979 | 44.9k | image->columns=jas_image_width(jp2_image); |
980 | 44.9k | image->rows=jas_image_height(jp2_image); |
981 | 44.9k | if (image->logging) |
982 | 44.9k | { |
983 | 44.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
984 | 44.9k | "columns=%lu rows=%lu components=%d",image->columns,image->rows, |
985 | 44.9k | number_components); |
986 | 90.9k | for (component=0; component < number_components; component++) |
987 | 45.9k | { |
988 | 45.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
989 | 45.9k | "Component %u:\n" |
990 | 45.9k | " width = %ld\n" /* width */ |
991 | 45.9k | " height = %ld\n" /* height */ |
992 | 45.9k | " tl x,y coordinate = %ld,%ld\n" /* x,y-coordinates of the top-left corner */ |
993 | 45.9k | " br x,y coordinate = %ld,%ld\n" /* x,y-coordinates of the bottom-right corner */ |
994 | 45.9k | " horizontal subsampling factor = %ld\n" /* horizontal subsampling factor */ |
995 | 45.9k | " vertical subsampling factor = %ld\n" /* vertical subsampling factor */ |
996 | 45.9k | " depth = %u\n" /* depth */ |
997 | 45.9k | " signed sample data = %d", /* signedness of the sample data (true == signed) */ |
998 | 45.9k | component, |
999 | 45.9k | (long) jas_image_cmptwidth(jp2_image,components[component]), |
1000 | 45.9k | (long) jas_image_cmptheight(jp2_image,components[component]), |
1001 | 45.9k | (long) jas_image_cmpttlx(jp2_image, components[component]), |
1002 | 45.9k | (long) jas_image_cmpttly(jp2_image, components[component]), |
1003 | 45.9k | (long) jas_image_cmptbrx(jp2_image, components[component]), |
1004 | 45.9k | (long) jas_image_cmptbry(jp2_image, components[component]), |
1005 | 45.9k | (long) jas_image_cmpthstep(jp2_image, components[component]), |
1006 | 45.9k | (long) jas_image_cmptvstep(jp2_image, components[component]), |
1007 | 45.9k | (unsigned int) jas_image_cmptprec(jp2_image,components[component]), |
1008 | 45.9k | (int) jas_image_cmptsgnd(jp2_image, components[component])); |
1009 | 45.9k | } |
1010 | 44.9k | } |
1011 | 90.0k | for (component=0; component < number_components; component++) |
1012 | 45.8k | { |
1013 | 45.8k | if (((unsigned long) jas_image_cmptwidth(jp2_image,components[component]) != image->columns) || |
1014 | 45.4k | ((unsigned long) jas_image_cmptheight(jp2_image,components[component]) != image->rows) || |
1015 | 45.1k | (jas_image_cmpttlx(jp2_image, components[component]) != 0) || |
1016 | 45.1k | (jas_image_cmpttly(jp2_image, components[component]) != 0) || |
1017 | 45.1k | (jas_image_cmpthstep(jp2_image, components[component]) != 1) || |
1018 | 45.1k | (jas_image_cmptvstep(jp2_image, components[component]) != 1)) |
1019 | 45.1k | ThrowJP2ReaderException(CoderError,IrregularChannelGeometryNotSupported,image); |
1020 | 45.1k | } |
1021 | | /* FIXME: It would be good to support signed data! */ |
1022 | 82.0k | for (component=0; component < number_components; component++) |
1023 | 45.0k | { |
1024 | 45.0k | if ((jas_image_cmptsgnd(jp2_image, components[component]) != false)) |
1025 | 37.8k | ThrowJP2ReaderException(CoderError,DataStorageTypeIsNotSupported,image); |
1026 | 37.8k | } |
1027 | | |
1028 | 36.9k | image->matte=number_components > 3; |
1029 | 74.8k | for (component=0; component < number_components; component++) |
1030 | 37.8k | { |
1031 | 37.8k | unsigned int |
1032 | 37.8k | component_depth; |
1033 | | |
1034 | 37.8k | component_depth=jas_image_cmptprec(jp2_image,components[component]); |
1035 | 37.8k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1036 | 37.8k | "Component[%d] depth is %u",component,component_depth); |
1037 | 37.8k | if (0 == component) |
1038 | 36.9k | image->depth=component_depth; |
1039 | 880 | else |
1040 | 880 | image->depth=Max(image->depth,component_depth); |
1041 | 37.8k | } |
1042 | 36.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1043 | 36.9k | "Image depth is %u",image->depth); |
1044 | 36.9k | if (image_info->ping) |
1045 | 0 | { |
1046 | 0 | (void) jas_stream_close(jp2_stream); |
1047 | 0 | jas_image_destroy(jp2_image); |
1048 | 0 | #if HAVE_JAS_INIT_LIBRARY |
1049 | | /* Perform any per-thread clean-up for the JasPer library. */ |
1050 | 0 | JAS_CLEANUP_THREAD(); |
1051 | 0 | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
1052 | 0 | return(image); |
1053 | 0 | } |
1054 | | |
1055 | 36.9k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
1056 | 32.9k | ThrowJP2ReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
1057 | | |
1058 | | /* |
1059 | | Allocate Jasper pixels. |
1060 | | */ |
1061 | 32.9k | pixels=jas_matrix_create(1,(unsigned int) image->columns); |
1062 | 32.9k | if (pixels == (jas_matrix_t *) NULL) |
1063 | 32.9k | ThrowJP2ReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1064 | | |
1065 | | /* |
1066 | | Allocate and populate channel LUTs |
1067 | | */ |
1068 | 66.0k | for (component=0; component < (long) number_components; component++) |
1069 | 33.7k | { |
1070 | 33.7k | double |
1071 | 33.7k | scale_to_quantum; |
1072 | | |
1073 | 33.7k | unsigned int |
1074 | 33.7k | component_depth, |
1075 | 33.7k | i, |
1076 | 33.7k | max_value; |
1077 | | |
1078 | 33.7k | component_depth=jas_image_cmptprec(jp2_image,components[component]); |
1079 | 33.7k | if ((0 == component_depth) || (component_depth > 16)) |
1080 | 682 | { |
1081 | 682 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1082 | 682 | "Component %u depth is %u!", |
1083 | 682 | component, component_depth); |
1084 | 682 | ThrowJP2ReaderException(CorruptImageError,ImproperImageHeader,image); |
1085 | 0 | } |
1086 | 33.0k | max_value=(unsigned int) MaxValueGivenBits(component_depth); |
1087 | 33.0k | if ((0 == max_value) || (max_value > 65535)) |
1088 | 33.0k | ThrowJP2ReaderException(CorruptImageError,ImproperImageHeader,image); |
1089 | 33.0k | scale_to_quantum=MaxRGBDouble/max_value; |
1090 | 33.0k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1091 | 33.0k | "Channel %d scale is %g", component, scale_to_quantum); |
1092 | 33.0k | channel_lut[component]=MagickAllocateResourceLimitedArray(Quantum *, (size_t) max_value+1,sizeof(Quantum)); |
1093 | 33.0k | if (channel_lut[component] == (Quantum *) NULL) |
1094 | 33.0k | ThrowJP2ReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1095 | 26.8M | for (i=0; i <= max_value; i++) |
1096 | 26.8M | (channel_lut[component])[i]=scale_to_quantum*i+0.5; |
1097 | 33.0k | } |
1098 | | |
1099 | | /* |
1100 | | Convert JPEG 2000 pixels. |
1101 | | */ |
1102 | 1.54M | for (y=0; y < (long) image->rows; y++) |
1103 | 1.52M | { |
1104 | 1.52M | q=GetImagePixels(image,0,y,image->columns,1); |
1105 | 1.52M | if (q == (PixelPacket *) NULL) |
1106 | 13.3k | break; |
1107 | | |
1108 | 1.50M | if (1 == number_components) |
1109 | 1.36M | { |
1110 | | /* Grayscale */ |
1111 | 1.36M | (void) jas_image_readcmpt(jp2_image,(short) components[0],0, |
1112 | 1.36M | (unsigned int) y, |
1113 | 1.36M | (unsigned int) image->columns,1,pixels); |
1114 | 1.40G | for (x=0; x < (long) image->columns; x++) |
1115 | 1.40G | { |
1116 | 1.40G | q->red=q->green=q->blue=(channel_lut[0])[jas_matrix_getv(pixels,x)]; |
1117 | 1.40G | q->opacity=OpaqueOpacity; |
1118 | 1.40G | q++; |
1119 | 1.40G | } |
1120 | 1.36M | } |
1121 | 144k | else |
1122 | 144k | { |
1123 | | /* Red */ |
1124 | 144k | (void) jas_image_readcmpt(jp2_image,(short) components[0],0, |
1125 | 144k | (unsigned int) y, |
1126 | 144k | (unsigned int) image->columns,1,pixels); |
1127 | 115M | for (x=0; x < (long) image->columns; x++) |
1128 | 115M | q[x].red=(channel_lut[0])[jas_matrix_getv(pixels,x)]; |
1129 | | |
1130 | | /* Green */ |
1131 | 144k | (void) jas_image_readcmpt(jp2_image,(short) components[1],0, |
1132 | 144k | (unsigned int) y, |
1133 | 144k | (unsigned int) image->columns,1,pixels); |
1134 | 115M | for (x=0; x < (long) image->columns; x++) |
1135 | 115M | q[x].green=(channel_lut[1])[jas_matrix_getv(pixels,x)]; |
1136 | | |
1137 | | /* Blue */ |
1138 | 144k | (void) jas_image_readcmpt(jp2_image,(short) components[2],0, |
1139 | 144k | (unsigned int) y, |
1140 | 144k | (unsigned int) image->columns,1,pixels); |
1141 | 115M | for (x=0; x < (long) image->columns; x++) |
1142 | 115M | q[x].blue=(channel_lut[2])[jas_matrix_getv(pixels,x)]; |
1143 | | |
1144 | | /* Opacity */ |
1145 | 144k | if (number_components > 3) |
1146 | 3.22k | { |
1147 | 3.22k | (void) jas_image_readcmpt(jp2_image,(short) components[3],0, |
1148 | 3.22k | (unsigned int) y, |
1149 | 3.22k | (unsigned int) image->columns,1,pixels); |
1150 | 435k | for (x=0; x < (long) image->columns; x++) |
1151 | 432k | q[x].opacity=MaxRGB-(channel_lut[3])[jas_matrix_getv(pixels,x)]; |
1152 | 3.22k | } |
1153 | 140k | else |
1154 | 140k | { |
1155 | 114M | for (x=0; x < (long) image->columns; x++) |
1156 | 114M | q[x].opacity=OpaqueOpacity; |
1157 | 140k | } |
1158 | 144k | } |
1159 | 1.50M | if (!SyncImagePixels(image)) |
1160 | 0 | break; |
1161 | 1.50M | if (image->previous == (Image *) NULL) |
1162 | 1.50M | if (QuantumTick(y,image->rows)) |
1163 | 209k | if (!MagickMonitorFormatted(y,image->rows,exception,LoadImageText, |
1164 | 209k | image->filename, |
1165 | 209k | image->columns,image->rows)) |
1166 | 0 | break; |
1167 | 1.50M | } |
1168 | 32.3k | if (number_components == 1) |
1169 | 31.9k | image->is_grayscale=MagickTrue; |
1170 | 32.3k | { |
1171 | | /* |
1172 | | Obtain ICC ICM color profile |
1173 | | */ |
1174 | | |
1175 | 32.3k | jas_cmprof_t |
1176 | 32.3k | *cm_profile; |
1177 | | |
1178 | | /* Obtain a pointer to the existing jas_cmprof_t profile handle. */ |
1179 | 32.3k | cm_profile=jas_image_cmprof(jp2_image); |
1180 | 32.3k | if (cm_profile != (jas_cmprof_t *) NULL) |
1181 | 32.3k | { |
1182 | 32.3k | jas_iccprof_t |
1183 | 32.3k | *icc_profile; |
1184 | | |
1185 | | /* Obtain a copy of the jas_iccprof_t ICC profile handle */ |
1186 | 32.3k | icc_profile=jas_iccprof_createfromcmprof(cm_profile); |
1187 | | /* or maybe just icc_profile=cm_profile->iccprof */ |
1188 | 32.3k | if (icc_profile != (jas_iccprof_t *) NULL) |
1189 | 32.3k | { |
1190 | 32.3k | jas_stream_t |
1191 | 32.3k | *icc_stream; |
1192 | | |
1193 | 32.3k | icc_stream=jas_stream_memopen(NULL,0); |
1194 | 32.3k | if ((icc_stream != (jas_stream_t *) NULL) && |
1195 | 32.3k | (jas_iccprof_save(icc_profile,icc_stream) == 0) && |
1196 | 32.3k | (jas_stream_flush(icc_stream) == 0)) |
1197 | 32.3k | { |
1198 | 32.3k | jas_stream_memobj_t |
1199 | 32.3k | *blob; |
1200 | | |
1201 | 32.3k | blob=(jas_stream_memobj_t *) icc_stream->obj_; |
1202 | 32.3k | if (image->logging) |
1203 | 32.3k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1204 | 32.3k | "ICC profile: %lu bytes",(unsigned long) blob->len_); |
1205 | 32.3k | SetImageProfile(image,"ICM",blob->buf_,blob->len_); |
1206 | | |
1207 | 32.3k | (void) jas_stream_close(icc_stream); |
1208 | 32.3k | jas_iccprof_destroy(icc_profile); |
1209 | 32.3k | } |
1210 | 32.3k | } |
1211 | 32.3k | } |
1212 | 32.3k | } |
1213 | | |
1214 | 65.3k | for (component=0; component < (long) number_components; component++) |
1215 | 33.0k | MagickFreeResourceLimitedMemory(Quantum *,channel_lut[component]); |
1216 | 32.3k | jas_matrix_destroy(pixels); |
1217 | 32.3k | (void) jas_stream_close(jp2_stream); |
1218 | 32.3k | MagickFreeMemory(options); |
1219 | 32.3k | jas_image_destroy(jp2_image); |
1220 | 32.3k | StopTimer(&image->timer); |
1221 | 32.3k | #if HAVE_JAS_INIT_LIBRARY |
1222 | | /* Perform any per-thread clean-up for the JasPer library. */ |
1223 | 32.3k | JAS_CLEANUP_THREAD(); |
1224 | 32.3k | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
1225 | 32.3k | return(image); |
1226 | 32.9k | } |
1227 | | #endif /* if defined(HasJP2) */ |
1228 | | |
1229 | | /* |
1230 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1231 | | % % |
1232 | | % % |
1233 | | % % |
1234 | | % R e g i s t e r J P 2 I m a g e % |
1235 | | % % |
1236 | | % % |
1237 | | % % |
1238 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1239 | | % |
1240 | | % Method RegisterJP2Image adds attributes for the JP2 image format to |
1241 | | % the list of supported formats. The attributes include the image format |
1242 | | % tag, a method to read and/or write the format, whether the format |
1243 | | % supports the saving of more than one frame to the same file or blob, |
1244 | | % whether the format supports native in-memory I/O, and a brief |
1245 | | % description of the format. |
1246 | | % |
1247 | | % The format of the RegisterJP2Image method is: |
1248 | | % |
1249 | | % RegisterJP2Image(void) |
1250 | | % |
1251 | | */ |
1252 | | ModuleExport void RegisterJP2Image(void) |
1253 | 10 | { |
1254 | 10 | #if defined(HasJP2) |
1255 | 10 | static char |
1256 | 10 | version[16]; |
1257 | | |
1258 | 10 | MagickInfo |
1259 | 10 | *entry; |
1260 | | |
1261 | 10 | (void) strlcpy(version,"JasPer ",sizeof(version)); |
1262 | 10 | (void) strlcat(version,jas_getversion(),sizeof(version)); |
1263 | | |
1264 | 10 | #if !defined(EXCLUDE_JP2_SUPPORT) || defined(JAS_ENABLE_JP2_CODEC) |
1265 | 10 | entry=SetMagickInfo("J2C"); |
1266 | 10 | entry->description="JPEG-2000 Code Stream Syntax"; |
1267 | 10 | entry->version=version; |
1268 | 10 | entry->module="JP2"; |
1269 | 10 | entry->magick=(MagickHandler) IsJPC; |
1270 | 10 | entry->adjoin=False; |
1271 | 10 | entry->seekable_stream=True; |
1272 | 10 | entry->thread_support=False; |
1273 | 10 | entry->decoder=(DecoderHandler) ReadJP2Image; |
1274 | 10 | entry->encoder=(EncoderHandler) WriteJP2Image; |
1275 | 10 | entry->coder_class=StableCoderClass; |
1276 | 10 | (void) RegisterMagickInfo(entry); |
1277 | 10 | #endif /* !defined(EXCLUDE_JP2_SUPPORT) || defined(JAS_ENABLE_JP2_CODEC) */ |
1278 | | |
1279 | 10 | #if !defined(EXCLUDE_JP2_SUPPORT) || defined(JAS_ENABLE_JP2_CODEC) |
1280 | 10 | entry=SetMagickInfo("JP2"); |
1281 | 10 | entry->description="JPEG-2000 JP2 File Format Syntax"; |
1282 | 10 | entry->version=version; |
1283 | 10 | entry->module="JP2"; |
1284 | 10 | entry->magick=(MagickHandler) IsJP2; |
1285 | 10 | entry->adjoin=False; |
1286 | 10 | entry->seekable_stream=True; |
1287 | 10 | entry->thread_support=False; |
1288 | 10 | entry->decoder=(DecoderHandler) ReadJP2Image; |
1289 | 10 | entry->encoder=(EncoderHandler) WriteJP2Image; |
1290 | 10 | entry->coder_class=StableCoderClass; |
1291 | 10 | (void) RegisterMagickInfo(entry); |
1292 | 10 | #endif /* !defined(EXCLUDE_JP2_SUPPORT) || defined(JAS_ENABLE_JP2_CODEC) */ |
1293 | | |
1294 | 10 | #if !defined(EXCLUDE_JPC_SUPPORT) || defined(JAS_ENABLE_JPC_CODEC) |
1295 | 10 | entry=SetMagickInfo("JPC"); |
1296 | 10 | entry->description="JPEG-2000 Code Stream Syntax"; |
1297 | 10 | entry->version=version; |
1298 | 10 | entry->module="JP2"; |
1299 | 10 | entry->magick=(MagickHandler) IsJPC; |
1300 | 10 | entry->adjoin=False; |
1301 | 10 | entry->seekable_stream=True; |
1302 | 10 | entry->thread_support=False; |
1303 | 10 | entry->decoder=(DecoderHandler) ReadJP2Image; |
1304 | 10 | entry->encoder=(EncoderHandler) WriteJP2Image; |
1305 | 10 | entry->coder_class=StableCoderClass; |
1306 | 10 | (void) RegisterMagickInfo(entry); |
1307 | 10 | #endif /* !defined(EXCLUDE_JPC_SUPPORT) || defined(JAS_ENABLE_JPC_CODEC) */ |
1308 | | |
1309 | 10 | #if !defined(EXCLUDE_PGX_SUPPORT) || defined(JAS_ENABLE_PGX_CODEC) |
1310 | 10 | entry=SetMagickInfo("PGX"); |
1311 | 10 | entry->description="JPEG-2000 VM Format"; |
1312 | 10 | entry->version=version; |
1313 | 10 | entry->module="JP2"; |
1314 | 10 | entry->magick=(MagickHandler) IsPGX; |
1315 | 10 | entry->adjoin=False; |
1316 | 10 | entry->seekable_stream=True; |
1317 | 10 | entry->thread_support=False; |
1318 | 10 | entry->decoder=(DecoderHandler) ReadJP2Image; |
1319 | 10 | entry->encoder=(EncoderHandler) WriteJP2Image; |
1320 | 10 | entry->coder_class=UnstableCoderClass; |
1321 | 10 | (void) RegisterMagickInfo(entry); |
1322 | 10 | #endif /* !defined(EXCLUDE_PGX_SUPPORT) || defined(JAS_ENABLE_PGX_CODEC) */ |
1323 | | |
1324 | 10 | #endif /* if defined(HasJP2) */ |
1325 | 10 | } |
1326 | | |
1327 | | /* |
1328 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1329 | | % % |
1330 | | % % |
1331 | | % % |
1332 | | % U n r e g i s t e r J P 2 I m a g e % |
1333 | | % % |
1334 | | % % |
1335 | | % % |
1336 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1337 | | % |
1338 | | % Method UnregisterJP2Image removes format registrations made by the |
1339 | | % JP2 module from the list of supported formats. |
1340 | | % |
1341 | | % The format of the UnregisterJP2Image method is: |
1342 | | % |
1343 | | % UnregisterJP2Image(void) |
1344 | | % |
1345 | | */ |
1346 | | ModuleExport void UnregisterJP2Image(void) |
1347 | 0 | { |
1348 | 0 | #if defined(HasJP2) |
1349 | 0 | (void) UnregisterMagickInfo("PGX"); |
1350 | 0 | (void) UnregisterMagickInfo("JPC"); |
1351 | 0 | (void) UnregisterMagickInfo("JP2"); |
1352 | 0 | (void) UnregisterMagickInfo("J2C"); |
1353 | | |
1354 | | /* |
1355 | | Cleanup Jasper |
1356 | | */ |
1357 | 0 | cleanup_jasper(); |
1358 | 0 | #endif /* if defined(HasJP2) */ |
1359 | 0 | } |
1360 | | |
1361 | | #if defined(HasJP2) |
1362 | | /* |
1363 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1364 | | % % |
1365 | | % % |
1366 | | % % |
1367 | | % W r i t e J P 2 I m a g e % |
1368 | | % % |
1369 | | % % |
1370 | | % % |
1371 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1372 | | % |
1373 | | % Method WriteJP2Image writes an image in the JPEG 2000 image format. |
1374 | | % |
1375 | | % JP2 support originally written by Nathan Brown, nathanbrown@letu.edu |
1376 | | % |
1377 | | % The format of the WriteJP2Image method is: |
1378 | | % |
1379 | | % MagickPassFail WriteJP2Image(const ImageInfo *image_info,Image *image) |
1380 | | % |
1381 | | % A description of each parameter follows. |
1382 | | % |
1383 | | % o status: Method WriteJP2Image return MagickTrue if the image is written. |
1384 | | % MagickFalse is returned is there is a memory shortage or if the image file |
1385 | | % fails to write. |
1386 | | % |
1387 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
1388 | | % |
1389 | | % o image: A pointer to an Image structure. |
1390 | | % |
1391 | | % |
1392 | | */ |
1393 | | static MagickPassFail |
1394 | | WriteJP2Image(const ImageInfo *image_info,Image *image) |
1395 | 2.16k | { |
1396 | 2.16k | char |
1397 | 2.16k | magick[MaxTextExtent], |
1398 | 2.16k | option_keyval[MaxTextExtent], |
1399 | 2.16k | *options = NULL; |
1400 | | |
1401 | 2.16k | int |
1402 | 2.16k | format; |
1403 | | |
1404 | 2.16k | long |
1405 | 2.16k | y; |
1406 | | |
1407 | 2.16k | jas_image_cmptparm_t |
1408 | 2.16k | component_info; |
1409 | | |
1410 | 2.16k | jas_image_t |
1411 | 2.16k | *jp2_image; |
1412 | | |
1413 | 2.16k | jas_matrix_t |
1414 | 2.16k | *jp2_pixels; |
1415 | | |
1416 | 2.16k | jas_stream_ops_t |
1417 | 2.16k | StreamOperators = |
1418 | 2.16k | { |
1419 | 2.16k | BlobRead, |
1420 | 2.16k | BlobWrite, |
1421 | 2.16k | BlobSeek, |
1422 | 2.16k | BlobClose |
1423 | 2.16k | }; |
1424 | | |
1425 | 2.16k | jas_stream_t |
1426 | 2.16k | *jp2_stream; |
1427 | | |
1428 | 2.16k | register const PixelPacket |
1429 | 2.16k | *p; |
1430 | | |
1431 | 2.16k | register int |
1432 | 2.16k | x; |
1433 | | |
1434 | 2.16k | MagickBool |
1435 | 2.16k | rate_specified=MagickFalse; |
1436 | | |
1437 | 2.16k | MagickPassFail |
1438 | 2.16k | status; |
1439 | | |
1440 | 2.16k | int |
1441 | 2.16k | component, |
1442 | 2.16k | number_components; |
1443 | | |
1444 | 2.16k | unsigned short |
1445 | 2.16k | *lut; |
1446 | | |
1447 | 2.16k | ImageCharacteristics |
1448 | 2.16k | characteristics; |
1449 | | |
1450 | | /* |
1451 | | Initialize Jasper |
1452 | | */ |
1453 | 2.16k | if (initialize_jasper(&image->exception) != MagickPass) |
1454 | 0 | { |
1455 | 0 | return MagickFail; |
1456 | 0 | } |
1457 | | |
1458 | 2.16k | #if HAVE_JAS_INIT_LIBRARY |
1459 | | /* |
1460 | | Perform any per-thread initialization for the JasPer library. |
1461 | | */ |
1462 | 2.16k | if (jas_init_thread()) |
1463 | 0 | { |
1464 | | /* Handle the initialization error. */ |
1465 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1466 | 0 | "jas_init_thread() failed!"); |
1467 | 0 | return MagickFail; |
1468 | 0 | } |
1469 | 2.16k | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
1470 | | |
1471 | | /* |
1472 | | Open image file. |
1473 | | */ |
1474 | 2.16k | assert(image_info != (const ImageInfo *) NULL); |
1475 | 2.16k | assert(image_info->signature == MagickSignature); |
1476 | 2.16k | assert(image != (Image *) NULL); |
1477 | 2.16k | assert(image->signature == MagickSignature); |
1478 | 2.16k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
1479 | 2.16k | if (status == MagickFail) |
1480 | 2.16k | ThrowJP2WriterException(FileOpenError,UnableToOpenFile,image); |
1481 | | |
1482 | | /* |
1483 | | Ensure that image is in RGB space. |
1484 | | */ |
1485 | 2.16k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1486 | 2.16k | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1487 | | |
1488 | | /* |
1489 | | PGX format requires a grayscale representation |
1490 | | */ |
1491 | 2.16k | if (strcmp("PGX",image_info->magick) == 0) |
1492 | 264 | { |
1493 | 264 | if (SetImageType(image,GrayscaleType) == MagickFail) |
1494 | 264 | ThrowWriterException(CoderError,UnableToSetImageType,image); |
1495 | 264 | } |
1496 | | |
1497 | | /* |
1498 | | Analyze image to be written. |
1499 | | */ |
1500 | 2.16k | if (!GetImageCharacteristics(image,&characteristics, |
1501 | 2.16k | (OptimizeType == image_info->type), |
1502 | 2.16k | &image->exception)) |
1503 | 0 | { |
1504 | 0 | CloseBlob(image); |
1505 | 0 | return MagickFail; |
1506 | 0 | } |
1507 | | |
1508 | | /* |
1509 | | Support passing Jasper options. |
1510 | | */ |
1511 | 2.16k | { |
1512 | 2.16k | unsigned int |
1513 | 2.16k | i; |
1514 | | |
1515 | 58.4k | for (i=0; i < ArraySize(jasper_enc_options); i++) |
1516 | 56.2k | { |
1517 | 56.2k | const char |
1518 | 56.2k | *option = jasper_enc_options[i]; |
1519 | | |
1520 | 56.2k | const char |
1521 | 56.2k | *value; |
1522 | | |
1523 | 56.2k | if ((value=AccessDefinition(image_info,"jp2",option)) != NULL) |
1524 | 0 | { |
1525 | 0 | if (LocaleCompare(option,"rate") == 0) |
1526 | 0 | { |
1527 | | /* |
1528 | | It is documented that a rate specification of 1.0 should |
1529 | | result in lossless compression. However, Jasper only |
1530 | | provides lossless compression if rate was not specified |
1531 | | at all. Support lossless compression as documented. |
1532 | | */ |
1533 | 0 | const double rate=atof(value); |
1534 | |
|
1535 | 0 | rate_specified=MagickTrue; |
1536 | 0 | if (rate < 1.0-MagickEpsilon) |
1537 | 0 | { |
1538 | 0 | MagickFormatString(option_keyval,sizeof(option_keyval),"%s=%.1024s ",option,value); |
1539 | 0 | ConcatenateString(&options,option_keyval); |
1540 | 0 | } |
1541 | 0 | } |
1542 | 0 | else |
1543 | 0 | { |
1544 | 0 | MagickFormatString(option_keyval,sizeof(option_keyval),"%s=%.1024s ",option,value); |
1545 | 0 | ConcatenateString(&options,option_keyval); |
1546 | | |
1547 | | /* Setting debug mode seems to require extra assistance */ |
1548 | 0 | if (LocaleCompare(option,"debug") == 0) |
1549 | 0 | jas_setdbglevel(atoi(value)); |
1550 | 0 | } |
1551 | 0 | } |
1552 | 56.2k | } |
1553 | 2.16k | } |
1554 | | |
1555 | | /* |
1556 | | Obtain a JP2 stream. |
1557 | | */ |
1558 | 2.16k | jp2_stream=JP2StreamManager(&StreamOperators, image); |
1559 | 2.16k | if (jp2_stream == (jas_stream_t *) NULL) |
1560 | 2.16k | ThrowJP2WriterException(DelegateError,UnableToManageJP2Stream,image); |
1561 | 2.16k | number_components=image->matte ? 4 : 3; |
1562 | 2.16k | if ((image_info->type != TrueColorType) && |
1563 | 2.16k | (characteristics.grayscale)) |
1564 | 1.78k | number_components=1; |
1565 | | |
1566 | 2.16k | jp2_image=jas_image_create0(); |
1567 | 2.16k | if (jp2_image == (jas_image_t *) NULL) |
1568 | 2.16k | ThrowJP2WriterException(DelegateError,UnableToCreateImage,image); |
1569 | | |
1570 | 5.09k | for (component=0; component < number_components; component++) |
1571 | 2.93k | { |
1572 | 2.93k | (void) memset((void *)&component_info,0,sizeof(jas_image_cmptparm_t)); |
1573 | 2.93k | component_info.tlx=0; /* top left x ordinate */ |
1574 | 2.93k | component_info.tly=0; /* top left y ordinate */ |
1575 | 2.93k | component_info.hstep=1; /* horizontal pixels per step */ |
1576 | 2.93k | component_info.vstep=1; /* vertical pixels per step */ |
1577 | 2.93k | component_info.width=(unsigned int) image->columns; |
1578 | 2.93k | component_info.height=(unsigned int) image->rows; |
1579 | 2.93k | component_info.prec=(unsigned int) Max(2,Min(image->depth,16)); /* bits in range */ |
1580 | 2.93k | component_info.sgnd = false; /* range is signed value? */ |
1581 | | |
1582 | 2.93k | if (jas_image_addcmpt(jp2_image, component,&component_info)) { |
1583 | 0 | jas_image_destroy(jp2_image); |
1584 | 0 | ThrowJP2WriterException(DelegateError,UnableToCreateImageComponent,image); |
1585 | 0 | } |
1586 | 2.93k | } |
1587 | | |
1588 | | /* |
1589 | | Allocate and compute LUT. |
1590 | | */ |
1591 | 2.16k | { |
1592 | 2.16k | unsigned long |
1593 | 2.16k | i, |
1594 | 2.16k | max_value; |
1595 | | |
1596 | 2.16k | double |
1597 | 2.16k | scale_to_component; |
1598 | | |
1599 | 2.16k | lut=MagickAllocateResourceLimitedArray(unsigned short *,MaxMap+1,sizeof(*lut)); |
1600 | 2.16k | if (lut == (unsigned short *) NULL) |
1601 | 0 | { |
1602 | 0 | jas_image_destroy(jp2_image); |
1603 | 0 | ThrowJP2WriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1604 | 0 | } |
1605 | | |
1606 | 2.16k | max_value=MaxValueGivenBits(component_info.prec); |
1607 | 2.16k | scale_to_component=max_value/MaxRGBDouble; |
1608 | 141M | for(i=0; i <= MaxMap; i++) |
1609 | 141M | lut[i]=scale_to_component*i+0.5; |
1610 | 2.16k | } |
1611 | | |
1612 | 2.16k | if (number_components == 1) |
1613 | 1.78k | { |
1614 | | /* FIXME: If image has an attached ICC profile, then the profile |
1615 | | should be transferred and the image colorspace set to |
1616 | | JAS_CLRSPC_GENGRAY */ |
1617 | | /* sRGB Grayscale */ |
1618 | 1.78k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1619 | 1.78k | "Setting SGRAY colorspace"); |
1620 | 1.78k | jas_image_setclrspc(jp2_image, JAS_CLRSPC_SGRAY); |
1621 | 1.78k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1622 | 1.78k | "Setting GRAY channel to channel 0"); |
1623 | 1.78k | jas_image_setcmpttype(jp2_image,0, |
1624 | 1.78k | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_GRAY_Y)); |
1625 | 1.78k | } |
1626 | 377 | else |
1627 | 377 | { |
1628 | | /* FIXME: If image has an attached ICC profile, then the profile |
1629 | | should be transferred and the image colorspace set to |
1630 | | JAS_CLRSPC_GENRGB */ |
1631 | | |
1632 | | /* sRGB */ |
1633 | 377 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1634 | 377 | "Setting SRGB colorspace"); |
1635 | 377 | jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB); |
1636 | 377 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1637 | 377 | "Setting RED channel to channel 0"); |
1638 | 377 | jas_image_setcmpttype(jp2_image,0, |
1639 | 377 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R)); |
1640 | 377 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1641 | 377 | "Setting GREEN channel to channel 1"); |
1642 | 377 | jas_image_setcmpttype(jp2_image,1, |
1643 | 377 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G)); |
1644 | 377 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1645 | 377 | "Setting BLUE channel to channel 2"); |
1646 | 377 | jas_image_setcmpttype(jp2_image,2, |
1647 | 377 | JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B)); |
1648 | 377 | if (number_components == 4 ) |
1649 | 16 | { |
1650 | 16 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1651 | 16 | "Setting OPACITY channel to channel 3"); |
1652 | 16 | jas_image_setcmpttype(jp2_image,3, |
1653 | 16 | JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_OPACITY)); |
1654 | 16 | } |
1655 | 377 | } |
1656 | | /* |
1657 | | Convert to JPEG 2000 pixels. |
1658 | | */ |
1659 | 2.16k | jp2_pixels=jas_matrix_create(1,(unsigned int) image->columns); |
1660 | 2.16k | if (jp2_pixels == (jas_matrix_t *) NULL) |
1661 | 0 | { |
1662 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,lut); |
1663 | 0 | jas_image_destroy(jp2_image); |
1664 | 0 | ThrowJP2WriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1665 | 0 | } |
1666 | | |
1667 | 1.49M | for (y=0; y < (long) image->rows; y++) |
1668 | 1.49M | { |
1669 | 1.49M | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1670 | 1.49M | if (p == (const PixelPacket *) NULL) |
1671 | 0 | break; |
1672 | 1.49M | if (number_components == 1) |
1673 | 1.34M | { |
1674 | 1.40G | for (x=0; x < (long) image->columns; x++) |
1675 | 1.40G | jas_matrix_setv(jp2_pixels,x,lut[ScaleQuantumToMap(PixelIntensityToQuantum(&p[x]))]); |
1676 | 1.34M | (void) jas_image_writecmpt(jp2_image,0,0,(unsigned int) y, |
1677 | 1.34M | (unsigned int) image->columns,1,jp2_pixels); |
1678 | 1.34M | } |
1679 | 144k | else |
1680 | 144k | { |
1681 | 115M | for (x=0; x < (long) image->columns; x++) |
1682 | 115M | jas_matrix_setv(jp2_pixels,x,lut[ScaleQuantumToMap(p[x].red)]); |
1683 | 144k | (void) jas_image_writecmpt(jp2_image,0,0,(unsigned int) y, |
1684 | 144k | (unsigned int) image->columns,1,jp2_pixels); |
1685 | | |
1686 | 115M | for (x=0; x < (long) image->columns; x++) |
1687 | 115M | jas_matrix_setv(jp2_pixels,x,lut[ScaleQuantumToMap(p[x].green)]); |
1688 | 144k | (void) jas_image_writecmpt(jp2_image,1,0,(unsigned int) y, |
1689 | 144k | (unsigned int) image->columns,1,jp2_pixels); |
1690 | | |
1691 | 115M | for (x=0; x < (long) image->columns; x++) |
1692 | 115M | jas_matrix_setv(jp2_pixels,x,lut[ScaleQuantumToMap(p[x].blue)]); |
1693 | 144k | (void) jas_image_writecmpt(jp2_image,2,0,(unsigned int) y, |
1694 | 144k | (unsigned int) image->columns,1,jp2_pixels); |
1695 | | |
1696 | 144k | if (number_components > 3) |
1697 | 435k | for (x=0; x < (long) image->columns; x++) |
1698 | 432k | jas_matrix_setv(jp2_pixels,x,lut[ScaleQuantumToMap(MaxRGB-p[x].opacity)]); |
1699 | 144k | (void) jas_image_writecmpt(jp2_image,3,0,(unsigned int) y, |
1700 | 144k | (unsigned int) image->columns,1,jp2_pixels); |
1701 | 144k | } |
1702 | 1.49M | if (image->previous == (Image *) NULL) |
1703 | 1.49M | if (QuantumTick(y,image->rows)) |
1704 | 192k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1705 | 192k | SaveImageText,image->filename, |
1706 | 192k | image->columns,image->rows)) |
1707 | 0 | break; |
1708 | 1.49M | } |
1709 | 2.16k | (void) strlcpy(magick,image_info->magick,MaxTextExtent); |
1710 | | /* |
1711 | | J2C is an alias for JPC but Jasper only supports "JPC". |
1712 | | */ |
1713 | 2.16k | if (LocaleCompare(magick,"j2c") == 0) |
1714 | 690 | (void) strlcpy(magick,"jpc",sizeof(magick)); |
1715 | 2.16k | LocaleLower(magick); |
1716 | 2.16k | format=jas_image_strtofmt(magick); |
1717 | | |
1718 | | /* |
1719 | | Provide an emulation of IJG JPEG "quality" by default if rate was |
1720 | | not specified. |
1721 | | */ |
1722 | 2.16k | if (rate_specified == MagickFalse) |
1723 | 2.16k | { |
1724 | 2.16k | double |
1725 | 2.16k | rate=INFINITY; |
1726 | | |
1727 | | /* |
1728 | | A rough approximation to JPEG v1 quality using JPEG-2000. |
1729 | | Default "quality" 75 results in a request for 16:1 compression, which |
1730 | | results in image sizes approximating that of JPEG v1. |
1731 | | */ |
1732 | 2.16k | if ((image_info->quality < 100.0-MagickEpsilon) && |
1733 | 2.16k | (MagickArraySize(image->rows,image->columns) > 2500U)) |
1734 | 1.75k | { |
1735 | 1.75k | double |
1736 | 1.75k | header_size, |
1737 | 1.75k | current_size, |
1738 | 1.75k | target_size, |
1739 | 1.75k | d; |
1740 | | |
1741 | 1.75k | d=115-image_info->quality; /* Best number is 110-115 */ |
1742 | 1.75k | rate=100.0/(d*d); |
1743 | 1.75k | header_size=550.0; /* Base file size. */ |
1744 | 1.75k | header_size+=((size_t) number_components-1)*142; /* Additional components */ |
1745 | | /* FIXME: Need to account for any ICC profiles here */ |
1746 | | |
1747 | 1.75k | current_size=(double)((image->rows*image->columns*image->depth)/8)* |
1748 | 1.75k | number_components; |
1749 | 1.75k | target_size=(current_size*rate)+header_size; |
1750 | 1.75k | rate=target_size/current_size; |
1751 | 1.75k | MagickFormatString(option_keyval,sizeof(option_keyval),"%s=%g ","rate",rate); |
1752 | 1.75k | ConcatenateString(&options,option_keyval); |
1753 | 1.75k | } |
1754 | 2.16k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1755 | 2.16k | "Compression rate: %g (%3.2f:1)",rate,1.0/rate); |
1756 | 2.16k | } |
1757 | | |
1758 | | |
1759 | 2.16k | if (options) |
1760 | 1.75k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1761 | 1.75k | "Jasper options: \"%s\"", options); |
1762 | | |
1763 | 2.16k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Encoding image"); |
1764 | 2.16k | status=jas_image_encode(jp2_image,jp2_stream,format,options); |
1765 | 2.16k | (void) jas_stream_close(jp2_stream); |
1766 | 2.16k | MagickFreeMemory(options); |
1767 | 2.16k | MagickFreeResourceLimitedMemory(unsigned short *,lut); |
1768 | 2.16k | jas_matrix_destroy(jp2_pixels); |
1769 | 2.16k | jas_image_destroy(jp2_image); |
1770 | 2.16k | if (status) |
1771 | 2.15k | ThrowJP2WriterException(DelegateError,UnableToEncodeImageFile,image); |
1772 | | |
1773 | 2.15k | #if HAVE_JAS_INIT_LIBRARY |
1774 | | /* Perform any per-thread clean-up for the JasPer library. */ |
1775 | 2.15k | JAS_CLEANUP_THREAD(); |
1776 | 2.15k | #endif /* if HAVE_JAS_INIT_LIBRARY */ |
1777 | 2.15k | return(True); |
1778 | 2.16k | } |
1779 | | #endif /* if defined(HasJP2) */ |