1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % JJJ X X L %
7 % J X X L %
8 % J X L %
9 % J J X X L %
10 % JJ X X LLLLL %
11 % %
12 % %
13 % Read/Write JPEG XL Lossless JPEG1 Recompression %
14 % %
15 % Dirk Lemstra %
16 % December 2020 %
17 % %
18 % %
19 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
20 % dedicated to making software imaging solutions freely available. %
21 % %
22 % You may not use this file except in compliance with the License. You may %
23 % obtain a copy of the License at %
24 % %
25 % https://imagemagick.org/script/license.php %
26 % %
27 % Unless required by applicable law or agreed to in writing, software %
28 % distributed under the License is distributed on an "AS IS" BASIS, %
29 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
30 % See the License for the specific language governing permissions and %
31 % limitations under the License. %
32 % %
33 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
34 %
35 %
36 */
37
38 /*
39 Include declarations.
40 */
41 #include "MagickCore/studio.h"
42 #include "MagickCore/attribute.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/blob-private.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/exception.h"
47 #include "MagickCore/exception-private.h"
48 #include "MagickCore/image.h"
49 #include "MagickCore/image-private.h"
50 #include "MagickCore/list.h"
51 #include "MagickCore/magick.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/monitor.h"
54 #include "MagickCore/monitor-private.h"
55 #include "MagickCore/resource_.h"
56 #include "MagickCore/static.h"
57 #include "MagickCore/string_.h"
58 #include "MagickCore/module.h"
59 #if defined(MAGICKCORE_JXL_DELEGATE)
60 #include <jxl/decode.h>
61 #include <jxl/encode.h>
62 #include <jxl/thread_parallel_runner.h>
63 #endif
64
65 /*
66 Typedef declarations.
67 */
68 typedef struct MemoryManagerInfo
69 {
70 Image
71 *image;
72
73 ExceptionInfo
74 *exception;
75 } MemoryManagerInfo;
76
77 /*
78 Forward declarations.
79 */
80 static MagickBooleanType
81 WriteJXLImage(const ImageInfo *,Image *,ExceptionInfo *);
82
83 #if defined(MAGICKCORE_JXL_DELEGATE)
JXLAcquireMemory(void * opaque,size_t size)84 static void *JXLAcquireMemory(void *opaque, size_t size)
85 {
86 unsigned char
87 *data;
88
89 data=(unsigned char *) AcquireQuantumMemory(size,sizeof(*data));
90 if (data == (unsigned char *) NULL)
91 {
92 MemoryManagerInfo
93 *memory_manager_info;
94
95 memory_manager_info=(MemoryManagerInfo *) opaque;
96 (void) ThrowMagickException(memory_manager_info->exception,
97 GetMagickModule(),CoderError,"MemoryAllocationFailed","`%s'",
98 memory_manager_info->image->filename);
99 }
100 return(data);
101 }
102
JXLRelinquishMemory(void * magick_unused (opaque),void * address)103 static void JXLRelinquishMemory(void *magick_unused(opaque),void *address)
104 {
105 magick_unreferenced(opaque);
106 (void) RelinquishMagickMemory(address);
107 }
108
JXLSetMemoryManager(JxlMemoryManager * memory_manager,MemoryManagerInfo * memory_manager_info,Image * image,ExceptionInfo * exception)109 static inline void JXLSetMemoryManager(JxlMemoryManager *memory_manager,
110 MemoryManagerInfo *memory_manager_info,Image *image,ExceptionInfo *exception)
111 {
112 memory_manager_info->image=image;
113 memory_manager_info->exception=exception;
114 memory_manager->opaque=memory_manager_info;
115 memory_manager->alloc=JXLAcquireMemory;
116 memory_manager->free=JXLRelinquishMemory;
117 }
118
JXLSetFormat(Image * image,JxlPixelFormat * format)119 static inline void JXLSetFormat(Image *image,JxlPixelFormat *format)
120 {
121 format->num_channels=(image->alpha_trait == BlendPixelTrait) ? 4 : 3;
122 format->data_type=(image->depth > 8) ? JXL_TYPE_FLOAT : JXL_TYPE_UINT8;
123 }
124
125 /*
126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 % %
128 % %
129 % %
130 % R e a d J X L I m a g e %
131 % %
132 % %
133 % %
134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135 %
136 % ReadJXLImage() reads a JXL image file and returns it. It allocates
137 % the memory necessary for the new Image structure and returns a pointer to
138 % the new image.
139 %
140 % The format of the ReadJXLImage method is:
141 %
142 % Image *ReadJXLImage(const ImageInfo *image_info,
143 % ExceptionInfo *exception)
144 %
145 % A description of each parameter follows:
146 %
147 % o image_info: the image info.
148 %
149 % o exception: return any errors or warnings in this structure.
150 %
151 */
JXLOrientationToOrientation(JxlOrientation orientation)152 static inline OrientationType JXLOrientationToOrientation(
153 JxlOrientation orientation)
154 {
155 switch (orientation)
156 {
157 default:
158 case JXL_ORIENT_IDENTITY:
159 return TopLeftOrientation;
160 case JXL_ORIENT_FLIP_HORIZONTAL:
161 return TopRightOrientation;
162 case JXL_ORIENT_ROTATE_180:
163 return BottomRightOrientation;
164 case JXL_ORIENT_FLIP_VERTICAL:
165 return BottomLeftOrientation;
166 case JXL_ORIENT_TRANSPOSE:
167 return LeftTopOrientation;
168 case JXL_ORIENT_ROTATE_90_CW:
169 return RightTopOrientation;
170 case JXL_ORIENT_ANTI_TRANSPOSE:
171 return RightBottomOrientation;
172 case JXL_ORIENT_ROTATE_90_CCW:
173 return LeftBottomOrientation;
174 }
175 }
176
ReadJXLImage(const ImageInfo * image_info,ExceptionInfo * exception)177 static Image *ReadJXLImage(const ImageInfo *image_info,ExceptionInfo *exception)
178 {
179 Image
180 *image;
181
182 JxlPixelFormat
183 format;
184
185 JxlDecoderStatus
186 events_wanted;
187
188 JxlDecoder
189 *decoder;
190
191 JxlDecoderStatus
192 decoder_status;
193
194 JxlMemoryManager
195 memory_manager;
196
197 MagickBooleanType
198 status;
199
200 MemoryManagerInfo
201 memory_manager_info;
202
203 size_t
204 input_size;
205
206 unsigned char
207 *input_buffer,
208 *output_buffer;
209
210 void
211 *runner;
212
213 /*
214 Open image file.
215 */
216 assert(image_info != (const ImageInfo *) NULL);
217 assert(image_info->signature == MagickCoreSignature);
218 if (image_info->debug != MagickFalse)
219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
220 image_info->filename);
221 assert(exception != (ExceptionInfo *) NULL);
222 assert(exception->signature == MagickCoreSignature);
223 image=AcquireImage(image_info, exception);
224 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
225 if (status == MagickFalse)
226 {
227 image=DestroyImageList(image);
228 return((Image *) NULL);
229 }
230 JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
231 decoder=JxlDecoderCreate(&memory_manager);
232 if (decoder == (JxlDecoder *) NULL)
233 ThrowReaderException(CoderError,"MemoryAllocationFailed");
234 runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
235 ThreadResource));
236 if (runner == (void *) NULL)
237 {
238 JxlDecoderDestroy(decoder);
239 ThrowWriterException(CoderError,"MemoryAllocationFailed");
240 }
241 decoder_status=JxlDecoderSetParallelRunner(decoder,JxlThreadParallelRunner,
242 runner);
243 if (decoder_status != JXL_DEC_SUCCESS)
244 {
245 JxlThreadParallelRunnerDestroy(runner);
246 JxlDecoderDestroy(decoder);
247 ThrowWriterException(CoderError,"MemoryAllocationFailed");
248 }
249 events_wanted=JXL_DEC_BASIC_INFO;
250 if (image_info->ping == MagickFalse)
251 events_wanted|=JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING;
252 if (JxlDecoderSubscribeEvents(decoder,events_wanted) != JXL_DEC_SUCCESS)
253 {
254 JxlThreadParallelRunnerDestroy(runner);
255 JxlDecoderDestroy(decoder);
256 ThrowReaderException(CoderError,"UnableToReadImageData");
257 }
258 input_size=MagickMaxBufferExtent;
259 input_buffer=AcquireQuantumMemory(input_size,sizeof(*input_buffer));
260 if (input_buffer == (unsigned char *) NULL)
261 {
262 JxlThreadParallelRunnerDestroy(runner);
263 JxlDecoderDestroy(decoder);
264 ThrowReaderException(CoderError,"MemoryAllocationFailed");
265 }
266 output_buffer=(unsigned char *) NULL;
267 memset(&format,0,sizeof(format));
268 decoder_status=JXL_DEC_NEED_MORE_INPUT;
269 while ((decoder_status != JXL_DEC_SUCCESS) &&
270 (decoder_status != JXL_DEC_ERROR))
271 {
272 decoder_status=JxlDecoderProcessInput(decoder);
273 switch (decoder_status)
274 {
275 case JXL_DEC_NEED_MORE_INPUT:
276 {
277 size_t
278 remaining;
279
280 ssize_t
281 count;
282
283 remaining=JxlDecoderReleaseInput(decoder);
284 if (remaining > 0)
285 memmove(input_buffer,input_buffer+input_size-remaining,remaining);
286 count=ReadBlob(image,input_size-remaining,input_buffer+remaining);
287 if (count <= 0)
288 {
289 decoder_status=JXL_DEC_SUCCESS;
290 ThrowMagickException(exception,GetMagickModule(),CoderError,
291 "InsufficientImageDataInFile","`%s'",image->filename);
292 break;
293 }
294 decoder_status=JxlDecoderSetInput(decoder,(const uint8_t *) input_buffer,
295 (size_t) count);
296 if (decoder_status == JXL_DEC_SUCCESS)
297 decoder_status=JXL_DEC_NEED_MORE_INPUT;
298 break;
299 }
300 case JXL_DEC_BASIC_INFO:
301 {
302 JxlBasicInfo
303 basic_info;
304
305 decoder_status=JxlDecoderGetBasicInfo(decoder,&basic_info);
306 if (decoder_status != JXL_DEC_SUCCESS)
307 break;
308 /* For now we dont support images with an animation */
309 if (basic_info.have_animation == 1)
310 {
311 ThrowMagickException(exception,GetMagickModule(),
312 MissingDelegateError,"NoDecodeDelegateForThisImageFormat",
313 "`%s'",image->filename);
314 break;
315 }
316 image->columns=basic_info.xsize;
317 image->rows=basic_info.ysize;
318 image->depth=basic_info.bits_per_sample;
319 if (basic_info.alpha_bits != 0)
320 image->alpha_trait=BlendPixelTrait;
321 image->orientation=JXLOrientationToOrientation(basic_info.orientation);
322 decoder_status=JXL_DEC_BASIC_INFO;
323 break;
324 }
325 case JXL_DEC_COLOR_ENCODING:
326 {
327 size_t
328 profile_size;
329
330 StringInfo
331 *profile;
332
333 decoder_status=JxlDecoderGetICCProfileSize(decoder,&format,
334 JXL_COLOR_PROFILE_TARGET_ORIGINAL,&profile_size);
335 if (decoder_status != JXL_DEC_SUCCESS)
336 break;
337 profile=AcquireStringInfo(profile_size);
338 decoder_status=JxlDecoderGetColorAsICCProfile(decoder,&format,
339 JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
340 profile_size);
341 if (decoder_status == JXL_DEC_SUCCESS)
342 decoder_status=JXL_DEC_COLOR_ENCODING;
343 break;
344 }
345 case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
346 {
347 size_t
348 output_size;
349
350 JXLSetFormat(image,&format);
351 decoder_status=JxlDecoderImageOutBufferSize(decoder,&format,
352 &output_size);
353 if (decoder_status != JXL_DEC_SUCCESS)
354 break;
355 status=SetImageExtent(image,image->columns,image->rows,exception);
356 if (status == MagickFalse)
357 break;
358 output_buffer=AcquireQuantumMemory(output_size,sizeof(*output_buffer));
359 if (output_buffer == (unsigned char *) NULL)
360 {
361 ThrowMagickException(exception,GetMagickModule(),CoderError,
362 "MemoryAllocationFailed","`%s'",image->filename);
363 break;
364 }
365 decoder_status=JxlDecoderSetImageOutBuffer(decoder,&format,
366 output_buffer,output_size);
367 if (decoder_status == JXL_DEC_SUCCESS)
368 decoder_status=JXL_DEC_NEED_IMAGE_OUT_BUFFER;
369 }
370 case JXL_DEC_FULL_IMAGE:
371 {
372 if (output_buffer == (unsigned char *) NULL)
373 {
374 ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
375 "UnableToReadImageData","`%s'",image->filename);
376 break;
377 }
378 status=ImportImagePixels(image,0,0,image->columns,image->rows,
379 image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",
380 format.data_type == JXL_TYPE_FLOAT ? FloatPixel : CharPixel,
381 output_buffer,exception);
382 if (status == MagickFalse)
383 decoder_status=JXL_DEC_ERROR;
384 break;
385 }
386 case JXL_DEC_SUCCESS:
387 case JXL_DEC_ERROR:
388 break;
389 default:
390 decoder_status=JXL_DEC_ERROR;
391 break;
392 }
393 }
394 output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
395 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
396 JxlThreadParallelRunnerDestroy(runner);
397 JxlDecoderDestroy(decoder);
398 if (decoder_status == JXL_DEC_ERROR)
399 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
400 return(image);
401 }
402 #endif
403
404 /*
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406 % %
407 % %
408 % %
409 % R e g i s t e r J X L I m a g e %
410 % %
411 % %
412 % %
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414 %
415 % RegisterJXLImage() adds properties for the JXL image format to
416 % the list of supported formats. The properties include the image format
417 % tag, a method to read and/or write the format, whether the format
418 % supports the saving of more than one frame to the same file or blob,
419 % whether the format supports native in-memory I/O, and a brief
420 % description of the format.
421 %
422 % The format of the RegisterJXLImage method is:
423 %
424 % size_t RegisterJXLImage(void)
425 %
426 */
RegisterJXLImage(void)427 ModuleExport size_t RegisterJXLImage(void)
428 {
429 MagickInfo
430 *entry;
431
432 entry=AcquireMagickInfo("JXL", "JXL", "JPEG XL Lossless JPEG1 Recompression");
433 #if defined(MAGICKCORE_JXL_DELEGATE)
434 entry->decoder=(DecodeImageHandler *) ReadJXLImage;
435 entry->encoder=(EncodeImageHandler *) WriteJXLImage;
436 #endif
437 entry->flags^=CoderAdjoinFlag;
438 (void) RegisterMagickInfo(entry);
439 return(MagickImageCoderSignature);
440 }
441
442 /*
443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444 % %
445 % %
446 % %
447 % U n r e g i s t e r J X L I m a g e %
448 % %
449 % %
450 % %
451 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452 %
453 % UnregisterJXLImage() removes format registrations made by the
454 % JXL module from the list of supported formats.
455 %
456 % The format of the UnregisterJXLImage method is:
457 %
458 % UnregisterJXLImage(void)
459 %
460 */
UnregisterJXLImage(void)461 ModuleExport void UnregisterJXLImage(void)
462 {
463 (void) UnregisterMagickInfo("JXL");
464 }
465
466 #if defined(MAGICKCORE_JXL_DELEGATE)
467 /*
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469 % %
470 % %
471 % %
472 % W r i t e J X L I m a g e %
473 % %
474 % %
475 % %
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %
478 % WriteJXLImage() writes a JXL image file and returns it. It
479 % allocates the memory necessary for the new Image structure and returns a
480 % pointer to the new image.
481 %
482 % The format of the WriteJXLImage method is:
483 %
484 % MagickBooleanType WriteJXLImage(const ImageInfo *image_info,
485 % Image *image)
486 %
487 % A description of each parameter follows:
488 %
489 % o image_info: the image info.
490 %
491 % o image: The image.
492 %
493 */
WriteJXLImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)494 static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
495 ExceptionInfo *exception)
496 {
497 JxlBasicInfo
498 basic_info;
499
500 JxlEncoder
501 *encoder;
502
503 JxlEncoderOptions
504 *encoder_options;
505
506 JxlEncoderStatus
507 encoder_status;
508
509 JxlMemoryManager
510 memory_manager;
511
512 JxlPixelFormat
513 format;
514
515 MagickBooleanType
516 status;
517
518 MemoryManagerInfo
519 memory_manager_info;
520
521 size_t
522 bytes_per_row;
523
524 unsigned char
525 *input_buffer;
526
527 void
528 *runner;
529
530 /*
531 Open output image file.
532 */
533 assert(image_info != (const ImageInfo *) NULL);
534 assert(image_info->signature == MagickCoreSignature);
535 assert(image != (Image *) NULL);
536 assert(image->signature == MagickCoreSignature);
537 if (image->debug != MagickFalse)
538 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
539 assert(exception != (ExceptionInfo *) NULL);
540 assert(exception->signature == MagickCoreSignature);
541 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
542 if (status == MagickFalse)
543 return(status);
544 JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
545 encoder=JxlEncoderCreate(&memory_manager);
546 if (encoder == (JxlEncoder *) NULL)
547 ThrowWriterException(CoderError,"MemoryAllocationFailed");
548 runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
549 ThreadResource));
550 if (runner == (void *) NULL)
551 {
552 JxlEncoderDestroy(encoder);
553 ThrowWriterException(CoderError,"MemoryAllocationFailed");
554 }
555 encoder_status=JxlEncoderSetParallelRunner(encoder,JxlThreadParallelRunner,
556 runner);
557 if (encoder_status != JXL_ENC_SUCCESS)
558 {
559 JxlThreadParallelRunnerDestroy(runner);
560 JxlEncoderDestroy(encoder);
561 return(MagickFalse);
562 }
563 memset(&format,0,sizeof(format));
564 JXLSetFormat(image,&format);
565 memset(&basic_info,0,sizeof(basic_info));
566 basic_info.xsize=(uint32_t) image->columns;
567 basic_info.ysize=(uint32_t) image->rows;
568 basic_info.bits_per_sample=8;
569 if (format.data_type == JXL_TYPE_FLOAT)
570 {
571 basic_info.bits_per_sample=32;
572 basic_info.exponent_bits_per_sample=8;
573 }
574 if (image->alpha_trait == BlendPixelTrait)
575 basic_info.alpha_bits=basic_info.bits_per_sample;
576 encoder_status=JxlEncoderSetBasicInfo(encoder,&basic_info);
577 if (encoder_status != JXL_ENC_SUCCESS)
578 {
579 JxlThreadParallelRunnerDestroy(runner);
580 JxlEncoderDestroy(encoder);
581 ThrowWriterException(CoderError,"UnableToWriteImageData");
582 }
583 encoder_options=JxlEncoderOptionsCreate(encoder,(JxlEncoderOptions *) NULL);
584 if (encoder_options == (JxlEncoderOptions *) NULL)
585 {
586 JxlThreadParallelRunnerDestroy(runner);
587 JxlEncoderDestroy(encoder);
588 ThrowWriterException(CoderError,"MemoryAllocationFailed");
589 }
590 if (image->quality == 100)
591 JxlEncoderOptionsSetLossless(encoder_options,JXL_TRUE);
592 bytes_per_row=image->columns*
593 ((image->alpha_trait == BlendPixelTrait) ? 4 : 3)*
594 ((format.data_type == JXL_TYPE_FLOAT) ? sizeof(float) : sizeof(char));
595 input_buffer=AcquireQuantumMemory(bytes_per_row,image->rows*
596 sizeof(*input_buffer));
597 if (input_buffer == (unsigned char *) NULL)
598 {
599 JxlThreadParallelRunnerDestroy(runner);
600 JxlEncoderDestroy(encoder);
601 ThrowWriterException(CoderError,"MemoryAllocationFailed");
602 }
603 status=ExportImagePixels(image,0,0,image->columns,image->rows,
604 image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",
605 format.data_type == JXL_TYPE_FLOAT ? FloatPixel : CharPixel,
606 input_buffer,exception);
607 if (status == MagickFalse)
608 {
609 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
610 JxlThreadParallelRunnerDestroy(runner);
611 JxlEncoderDestroy(encoder);
612 ThrowWriterException(CoderError,"MemoryAllocationFailed");
613 }
614 encoder_status=JxlEncoderAddImageFrame(encoder_options,&format,input_buffer,
615 bytes_per_row*image->rows);
616 if (encoder_status == JXL_ENC_SUCCESS)
617 {
618 unsigned char
619 *output_buffer;
620
621 output_buffer=AcquireQuantumMemory(MagickMaxBufferExtent,
622 sizeof(*output_buffer));
623 if (output_buffer == (unsigned char *) NULL)
624 {
625 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
626 JxlThreadParallelRunnerDestroy(runner);
627 JxlEncoderDestroy(encoder);
628 ThrowWriterException(CoderError,"MemoryAllocationFailed");
629 }
630 encoder_status=JXL_ENC_NEED_MORE_OUTPUT;
631 while (encoder_status == JXL_ENC_NEED_MORE_OUTPUT)
632 {
633 size_t
634 count;
635
636 unsigned char
637 *p;
638
639 count=MagickMaxBufferExtent;
640 p=output_buffer;
641 encoder_status=JxlEncoderProcessOutput(encoder,&p,&count);
642 (void) WriteBlob(image,MagickMaxBufferExtent-count,output_buffer);
643 }
644 output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
645 }
646 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
647 JxlThreadParallelRunnerDestroy(runner);
648 JxlEncoderDestroy(encoder);
649 if (encoder_status != JXL_ENC_SUCCESS)
650 ThrowWriterException(CoderError,"UnableToWriteImageData");
651 (void) CloseBlob(image);
652 return(status);
653 }
654 #endif
655