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 % JPEG XL (ISO/IEC 18181) %
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/option.h"
56 #include "MagickCore/resource_.h"
57 #include "MagickCore/static.h"
58 #include "MagickCore/string_.h"
59 #include "MagickCore/string-private.h"
60 #include "MagickCore/module.h"
61 #if defined(MAGICKCORE_JXL_DELEGATE)
62 #include <jxl/decode.h>
63 #include <jxl/encode.h>
64 #include <jxl/thread_parallel_runner.h>
65 #endif
66
67 /*
68 Typedef declarations.
69 */
70 typedef struct MemoryManagerInfo
71 {
72 Image
73 *image;
74
75 ExceptionInfo
76 *exception;
77 } MemoryManagerInfo;
78
79 /*
80 Forward declarations.
81 */
82 static MagickBooleanType
83 WriteJXLImage(const ImageInfo *,Image *,ExceptionInfo *);
84
85 #if defined(MAGICKCORE_JXL_DELEGATE)
JXLAcquireMemory(void * opaque,size_t size)86 static void *JXLAcquireMemory(void *opaque, size_t size)
87 {
88 unsigned char
89 *data;
90
91 data=(unsigned char *) AcquireQuantumMemory(size,sizeof(*data));
92 if (data == (unsigned char *) NULL)
93 {
94 MemoryManagerInfo
95 *memory_manager_info;
96
97 memory_manager_info=(MemoryManagerInfo *) opaque;
98 (void) ThrowMagickException(memory_manager_info->exception,
99 GetMagickModule(),CoderError,"MemoryAllocationFailed","`%s'",
100 memory_manager_info->image->filename);
101 }
102 return(data);
103 }
104
JXLRelinquishMemory(void * magick_unused (opaque),void * address)105 static void JXLRelinquishMemory(void *magick_unused(opaque),void *address)
106 {
107 magick_unreferenced(opaque);
108 (void) RelinquishMagickMemory(address);
109 }
110
JXLSetMemoryManager(JxlMemoryManager * memory_manager,MemoryManagerInfo * memory_manager_info,Image * image,ExceptionInfo * exception)111 static inline void JXLSetMemoryManager(JxlMemoryManager *memory_manager,
112 MemoryManagerInfo *memory_manager_info,Image *image,ExceptionInfo *exception)
113 {
114 memory_manager_info->image=image;
115 memory_manager_info->exception=exception;
116 memory_manager->opaque=memory_manager_info;
117 memory_manager->alloc=JXLAcquireMemory;
118 memory_manager->free=JXLRelinquishMemory;
119 }
120
JXLSetFormat(Image * image,JxlPixelFormat * format)121 static inline void JXLSetFormat(Image *image,JxlPixelFormat *format)
122 {
123 format->num_channels=(image->alpha_trait == BlendPixelTrait) ? 4 : 3;
124 format->data_type=(image->depth > 16) ? JXL_TYPE_FLOAT : (image->depth > 8) ?
125 JXL_TYPE_UINT16 : JXL_TYPE_UINT8;
126 }
127
128 /*
129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
130 % %
131 % %
132 % %
133 % R e a d J X L I m a g e %
134 % %
135 % %
136 % %
137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138 %
139 % ReadJXLImage() reads a JXL image file and returns it. It allocates
140 % the memory necessary for the new Image structure and returns a pointer to
141 % the new image.
142 %
143 % The format of the ReadJXLImage method is:
144 %
145 % Image *ReadJXLImage(const ImageInfo *image_info,
146 % ExceptionInfo *exception)
147 %
148 % A description of each parameter follows:
149 %
150 % o image_info: the image info.
151 %
152 % o exception: return any errors or warnings in this structure.
153 %
154 */
JXLOrientationToOrientation(JxlOrientation orientation)155 static inline OrientationType JXLOrientationToOrientation(
156 JxlOrientation orientation)
157 {
158 switch (orientation)
159 {
160 default:
161 case JXL_ORIENT_IDENTITY:
162 return TopLeftOrientation;
163 case JXL_ORIENT_FLIP_HORIZONTAL:
164 return TopRightOrientation;
165 case JXL_ORIENT_ROTATE_180:
166 return BottomRightOrientation;
167 case JXL_ORIENT_FLIP_VERTICAL:
168 return BottomLeftOrientation;
169 case JXL_ORIENT_TRANSPOSE:
170 return LeftTopOrientation;
171 case JXL_ORIENT_ROTATE_90_CW:
172 return RightTopOrientation;
173 case JXL_ORIENT_ANTI_TRANSPOSE:
174 return RightBottomOrientation;
175 case JXL_ORIENT_ROTATE_90_CCW:
176 return LeftBottomOrientation;
177 }
178 }
179
JXLDataTypeToStorageType(const JxlDataType data_type)180 static inline StorageType JXLDataTypeToStorageType(const JxlDataType data_type)
181 {
182 switch (data_type)
183 {
184 case JXL_TYPE_FLOAT:
185 return FloatPixel;
186 case JXL_TYPE_UINT16:
187 return ShortPixel;
188 case JXL_TYPE_UINT8:
189 return CharPixel;
190 default:
191 return UndefinedPixel;
192 }
193 }
194
ReadJXLImage(const ImageInfo * image_info,ExceptionInfo * exception)195 static Image *ReadJXLImage(const ImageInfo *image_info,ExceptionInfo *exception)
196 {
197 Image
198 *image;
199
200 JxlPixelFormat
201 format;
202
203 JxlDecoderStatus
204 events_wanted;
205
206 JxlDecoder
207 *decoder;
208
209 JxlDecoderStatus
210 decoder_status;
211
212 JxlMemoryManager
213 memory_manager;
214
215 MagickBooleanType
216 status;
217
218 MemoryManagerInfo
219 memory_manager_info;
220
221 size_t
222 input_size;
223
224 unsigned char
225 *input_buffer,
226 *output_buffer;
227
228 void
229 *runner;
230
231 /*
232 Open image file.
233 */
234 assert(image_info != (const ImageInfo *) NULL);
235 assert(image_info->signature == MagickCoreSignature);
236 if (image_info->debug != MagickFalse)
237 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
238 image_info->filename);
239 assert(exception != (ExceptionInfo *) NULL);
240 assert(exception->signature == MagickCoreSignature);
241 image=AcquireImage(image_info, exception);
242 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
243 if (status == MagickFalse)
244 {
245 image=DestroyImageList(image);
246 return((Image *) NULL);
247 }
248 JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
249 decoder=JxlDecoderCreate(&memory_manager);
250 if (decoder == (JxlDecoder *) NULL)
251 ThrowReaderException(CoderError,"MemoryAllocationFailed");
252 runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
253 ThreadResource));
254 if (runner == (void *) NULL)
255 {
256 JxlDecoderDestroy(decoder);
257 ThrowReaderException(CoderError,"MemoryAllocationFailed");
258 }
259 decoder_status=JxlDecoderSetParallelRunner(decoder,JxlThreadParallelRunner,
260 runner);
261 if (decoder_status != JXL_DEC_SUCCESS)
262 {
263 JxlThreadParallelRunnerDestroy(runner);
264 JxlDecoderDestroy(decoder);
265 ThrowReaderException(CoderError,"MemoryAllocationFailed");
266 }
267 events_wanted=JXL_DEC_BASIC_INFO;
268 if (image_info->ping == MagickFalse)
269 events_wanted|=JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING;
270 if (JxlDecoderSubscribeEvents(decoder,events_wanted) != JXL_DEC_SUCCESS)
271 {
272 JxlThreadParallelRunnerDestroy(runner);
273 JxlDecoderDestroy(decoder);
274 ThrowReaderException(CoderError,"UnableToReadImageData");
275 }
276 input_size=MagickMaxBufferExtent;
277 input_buffer=AcquireQuantumMemory(input_size,sizeof(*input_buffer));
278 if (input_buffer == (unsigned char *) NULL)
279 {
280 JxlThreadParallelRunnerDestroy(runner);
281 JxlDecoderDestroy(decoder);
282 ThrowReaderException(CoderError,"MemoryAllocationFailed");
283 }
284 output_buffer=(unsigned char *) NULL;
285 memset(&format,0,sizeof(format));
286 decoder_status=JXL_DEC_NEED_MORE_INPUT;
287 while ((decoder_status != JXL_DEC_SUCCESS) &&
288 (decoder_status != JXL_DEC_ERROR))
289 {
290 decoder_status=JxlDecoderProcessInput(decoder);
291 switch (decoder_status)
292 {
293 case JXL_DEC_NEED_MORE_INPUT:
294 {
295 size_t
296 remaining;
297
298 ssize_t
299 count;
300
301 remaining=JxlDecoderReleaseInput(decoder);
302 if (remaining > 0)
303 memmove(input_buffer,input_buffer+input_size-remaining,remaining);
304 count=ReadBlob(image,input_size-remaining,input_buffer+remaining);
305 if (count <= 0)
306 {
307 decoder_status=JXL_DEC_SUCCESS;
308 ThrowMagickException(exception,GetMagickModule(),CoderError,
309 "InsufficientImageDataInFile","`%s'",image->filename);
310 break;
311 }
312 decoder_status=JxlDecoderSetInput(decoder,(const uint8_t *) input_buffer,
313 (size_t) count);
314 if (decoder_status == JXL_DEC_SUCCESS)
315 decoder_status=JXL_DEC_NEED_MORE_INPUT;
316 break;
317 }
318 case JXL_DEC_BASIC_INFO:
319 {
320 JxlBasicInfo
321 basic_info;
322
323 decoder_status=JxlDecoderGetBasicInfo(decoder,&basic_info);
324 if (decoder_status != JXL_DEC_SUCCESS)
325 break;
326 /* For now we dont support images with an animation */
327 if (basic_info.have_animation == 1)
328 {
329 ThrowMagickException(exception,GetMagickModule(),
330 MissingDelegateError,"NoDecodeDelegateForThisImageFormat",
331 "`%s'",image->filename);
332 break;
333 }
334 image->columns=basic_info.xsize;
335 image->rows=basic_info.ysize;
336 image->depth=basic_info.bits_per_sample;
337 if (basic_info.alpha_bits != 0)
338 image->alpha_trait=BlendPixelTrait;
339 image->orientation=JXLOrientationToOrientation(basic_info.orientation);
340 decoder_status=JXL_DEC_BASIC_INFO;
341 break;
342 }
343 case JXL_DEC_COLOR_ENCODING:
344 {
345 size_t
346 profile_size;
347
348 StringInfo
349 *profile;
350
351 decoder_status=JxlDecoderGetICCProfileSize(decoder,&format,
352 JXL_COLOR_PROFILE_TARGET_ORIGINAL,&profile_size);
353 if (decoder_status != JXL_DEC_SUCCESS)
354 break;
355 profile=AcquireStringInfo(profile_size);
356 decoder_status=JxlDecoderGetColorAsICCProfile(decoder,&format,
357 JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
358 profile_size);
359 (void) SetImageProfile(image,"icc",profile,exception);
360 DestroyStringInfo(profile);
361 if (decoder_status == JXL_DEC_SUCCESS)
362 decoder_status=JXL_DEC_COLOR_ENCODING;
363 break;
364 }
365 case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
366 {
367 size_t
368 output_size;
369
370 JXLSetFormat(image,&format);
371 decoder_status=JxlDecoderImageOutBufferSize(decoder,&format,
372 &output_size);
373 if (decoder_status != JXL_DEC_SUCCESS)
374 break;
375 status=SetImageExtent(image,image->columns,image->rows,exception);
376 if (status == MagickFalse)
377 break;
378 output_buffer=AcquireQuantumMemory(output_size,sizeof(*output_buffer));
379 if (output_buffer == (unsigned char *) NULL)
380 {
381 ThrowMagickException(exception,GetMagickModule(),CoderError,
382 "MemoryAllocationFailed","`%s'",image->filename);
383 break;
384 }
385 decoder_status=JxlDecoderSetImageOutBuffer(decoder,&format,
386 output_buffer,output_size);
387 if (decoder_status == JXL_DEC_SUCCESS)
388 decoder_status=JXL_DEC_NEED_IMAGE_OUT_BUFFER;
389 }
390 case JXL_DEC_FULL_IMAGE:
391 {
392 StorageType
393 type;
394
395 if (output_buffer == (unsigned char *) NULL)
396 {
397 ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
398 "UnableToReadImageData","`%s'",image->filename);
399 break;
400 }
401 type=JXLDataTypeToStorageType(format.data_type);
402 if (type == UndefinedPixel)
403 {
404 ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
405 "Unsupported data type","`%s'",image->filename);
406 break;
407 }
408 status=ImportImagePixels(image,0,0,image->columns,image->rows,
409 image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",type,
410 output_buffer,exception);
411 if (status == MagickFalse)
412 decoder_status=JXL_DEC_ERROR;
413 break;
414 }
415 case JXL_DEC_SUCCESS:
416 case JXL_DEC_ERROR:
417 break;
418 default:
419 decoder_status=JXL_DEC_ERROR;
420 break;
421 }
422 }
423 output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
424 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
425 JxlThreadParallelRunnerDestroy(runner);
426 JxlDecoderDestroy(decoder);
427 if (decoder_status == JXL_DEC_ERROR)
428 ThrowReaderException(CorruptImageError,"UnableToReadImageData");
429 (void) CloseBlob(image);
430 return(image);
431 }
432 #endif
433
434 /*
435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 % %
437 % %
438 % %
439 % R e g i s t e r J X L I m a g e %
440 % %
441 % %
442 % %
443 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
444 %
445 % RegisterJXLImage() adds properties for the JXL image format to
446 % the list of supported formats. The properties include the image format
447 % tag, a method to read and/or write the format, whether the format
448 % supports the saving of more than one frame to the same file or blob,
449 % whether the format supports native in-memory I/O, and a brief
450 % description of the format.
451 %
452 % The format of the RegisterJXLImage method is:
453 %
454 % size_t RegisterJXLImage(void)
455 %
456 */
RegisterJXLImage(void)457 ModuleExport size_t RegisterJXLImage(void)
458 {
459 MagickInfo
460 *entry;
461
462 entry=AcquireMagickInfo("JXL", "JXL", "JPEG XL (ISO/IEC 18181)");
463 #if defined(MAGICKCORE_JXL_DELEGATE)
464 entry->decoder=(DecodeImageHandler *) ReadJXLImage;
465 entry->encoder=(EncodeImageHandler *) WriteJXLImage;
466 #endif
467 entry->flags^=CoderAdjoinFlag;
468 (void) RegisterMagickInfo(entry);
469 return(MagickImageCoderSignature);
470 }
471
472 /*
473 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 % %
475 % %
476 % %
477 % U n r e g i s t e r J X L I m a g e %
478 % %
479 % %
480 % %
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 %
483 % UnregisterJXLImage() removes format registrations made by the
484 % JXL module from the list of supported formats.
485 %
486 % The format of the UnregisterJXLImage method is:
487 %
488 % UnregisterJXLImage(void)
489 %
490 */
UnregisterJXLImage(void)491 ModuleExport void UnregisterJXLImage(void)
492 {
493 (void) UnregisterMagickInfo("JXL");
494 }
495
496 #if defined(MAGICKCORE_JXL_DELEGATE)
497 /*
498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
499 % %
500 % %
501 % %
502 % W r i t e J X L I m a g e %
503 % %
504 % %
505 % %
506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507 %
508 % WriteJXLImage() writes a JXL image file and returns it. It
509 % allocates the memory necessary for the new Image structure and returns a
510 % pointer to the new image.
511 %
512 % The format of the WriteJXLImage method is:
513 %
514 % MagickBooleanType WriteJXLImage(const ImageInfo *image_info,
515 % Image *image)
516 %
517 % A description of each parameter follows:
518 %
519 % o image_info: the image info.
520 %
521 % o image: The image.
522 %
523 */
524
JXLWriteMetadata(const Image * image,JxlEncoder * encoder)525 static JxlEncoderStatus JXLWriteMetadata(const Image *image,
526 JxlEncoder *encoder)
527 {
528 JxlColorEncoding
529 color_encoding;
530
531 JxlEncoderStatus
532 encoder_status;
533
534 memset(&color_encoding,0,sizeof(color_encoding));
535 JxlColorEncodingSetToSRGB(&color_encoding,
536 IsImageGray(image) == MagickTrue ? JXL_TRUE : JXL_FALSE);
537 encoder_status=JxlEncoderSetColorEncoding(encoder,&color_encoding);
538 return(encoder_status);
539 }
540
WriteJXLImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)541 static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
542 ExceptionInfo *exception)
543 {
544 const char
545 *option;
546
547 JxlBasicInfo
548 basic_info;
549
550 JxlEncoder
551 *encoder;
552
553 JxlEncoderOptions
554 *encoder_options;
555
556 JxlEncoderStatus
557 encoder_status;
558
559 JxlMemoryManager
560 memory_manager;
561
562 JxlPixelFormat
563 format;
564
565 MagickBooleanType
566 status;
567
568 MemoryManagerInfo
569 memory_manager_info;
570
571 size_t
572 bytes_per_row;
573
574 unsigned char
575 *input_buffer;
576
577 void
578 *runner;
579
580 /*
581 Open output image file.
582 */
583 assert(image_info != (const ImageInfo *) NULL);
584 assert(image_info->signature == MagickCoreSignature);
585 assert(image != (Image *) NULL);
586 assert(image->signature == MagickCoreSignature);
587 if (image->debug != MagickFalse)
588 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
589 assert(exception != (ExceptionInfo *) NULL);
590 assert(exception->signature == MagickCoreSignature);
591 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
592 if (status == MagickFalse)
593 return(status);
594 JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
595 encoder=JxlEncoderCreate(&memory_manager);
596 if (encoder == (JxlEncoder *) NULL)
597 ThrowWriterException(CoderError,"MemoryAllocationFailed");
598 runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
599 ThreadResource));
600 if (runner == (void *) NULL)
601 {
602 JxlEncoderDestroy(encoder);
603 ThrowWriterException(CoderError,"MemoryAllocationFailed");
604 }
605 encoder_status=JxlEncoderSetParallelRunner(encoder,JxlThreadParallelRunner,
606 runner);
607 if (encoder_status != JXL_ENC_SUCCESS)
608 {
609 JxlThreadParallelRunnerDestroy(runner);
610 JxlEncoderDestroy(encoder);
611 return(MagickFalse);
612 }
613 memset(&format,0,sizeof(format));
614 JXLSetFormat(image,&format);
615 JxlEncoderInitBasicInfo(&basic_info);
616 basic_info.xsize=(uint32_t) image->columns;
617 basic_info.ysize=(uint32_t) image->rows;
618 basic_info.bits_per_sample=8;
619 if (format.data_type == JXL_TYPE_UINT16)
620 basic_info.bits_per_sample=16;
621 else if (format.data_type == JXL_TYPE_FLOAT)
622 {
623 basic_info.bits_per_sample=32;
624 basic_info.exponent_bits_per_sample=8;
625 }
626 if (image->alpha_trait == BlendPixelTrait)
627 basic_info.alpha_bits=basic_info.bits_per_sample;
628 encoder_status=JxlEncoderSetBasicInfo(encoder,&basic_info);
629 if (encoder_status != JXL_ENC_SUCCESS)
630 {
631 JxlThreadParallelRunnerDestroy(runner);
632 JxlEncoderDestroy(encoder);
633 ThrowWriterException(CoderError,"UnableToWriteImageData");
634 }
635 encoder_options=JxlEncoderOptionsCreate(encoder,(JxlEncoderOptions *) NULL);
636 if (encoder_options == (JxlEncoderOptions *) NULL)
637 {
638 JxlThreadParallelRunnerDestroy(runner);
639 JxlEncoderDestroy(encoder);
640 ThrowWriterException(CoderError,"MemoryAllocationFailed");
641 }
642 if (image->quality == 100)
643 JxlEncoderOptionsSetLossless(encoder_options,JXL_TRUE);
644 else
645 {
646 float
647 distance;
648
649 distance=(image_info->quality >= 30) ? 0.1f+(float) (100-MagickMin(100,
650 image_info->quality))*0.09f : 6.4f+(float) pow(2.5f,(30-
651 image_info->quality)/5.0f)/6.25f;
652 JxlEncoderOptionsSetDistance(encoder_options,distance);
653 }
654 option=GetImageOption(image_info,"jxl:effort");
655 if (option != (const char *) NULL)
656 JxlEncoderOptionsSetEffort(encoder_options,StringToInteger(option));
657 encoder_status=JXLWriteMetadata(image,encoder);
658 encoder_status=JXL_ENC_SUCCESS;
659 if (encoder_status != JXL_ENC_SUCCESS)
660 {
661 JxlThreadParallelRunnerDestroy(runner);
662 JxlEncoderDestroy(encoder);
663 ThrowWriterException(CoderError,"UnableToWriteImageData");
664 }
665 bytes_per_row=image->columns*
666 ((image->alpha_trait == BlendPixelTrait) ? 4 : 3)*
667 ((format.data_type == JXL_TYPE_FLOAT) ? sizeof(float) :
668 (format.data_type == JXL_TYPE_UINT16) ? sizeof(short) : sizeof(char));
669 input_buffer=AcquireQuantumMemory(bytes_per_row,image->rows*
670 sizeof(*input_buffer));
671 if (input_buffer == (unsigned char *) NULL)
672 {
673 JxlThreadParallelRunnerDestroy(runner);
674 JxlEncoderDestroy(encoder);
675 ThrowWriterException(CoderError,"MemoryAllocationFailed");
676 }
677 status=ExportImagePixels(image,0,0,image->columns,image->rows,
678 image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",
679 JXLDataTypeToStorageType(format.data_type),input_buffer,exception);
680 if (status == MagickFalse)
681 {
682 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
683 JxlThreadParallelRunnerDestroy(runner);
684 JxlEncoderDestroy(encoder);
685 ThrowWriterException(CoderError,"MemoryAllocationFailed");
686 }
687 encoder_status=JxlEncoderAddImageFrame(encoder_options,&format,input_buffer,
688 bytes_per_row*image->rows);
689 if (encoder_status == JXL_ENC_SUCCESS)
690 {
691 unsigned char
692 *output_buffer;
693
694 output_buffer=AcquireQuantumMemory(MagickMaxBufferExtent,
695 sizeof(*output_buffer));
696 if (output_buffer == (unsigned char *) NULL)
697 {
698 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
699 JxlThreadParallelRunnerDestroy(runner);
700 JxlEncoderDestroy(encoder);
701 ThrowWriterException(CoderError,"MemoryAllocationFailed");
702 }
703 encoder_status=JXL_ENC_NEED_MORE_OUTPUT;
704 while (encoder_status == JXL_ENC_NEED_MORE_OUTPUT)
705 {
706 size_t
707 count;
708
709 unsigned char
710 *p;
711
712 count=MagickMaxBufferExtent;
713 p=output_buffer;
714 encoder_status=JxlEncoderProcessOutput(encoder,&p,&count);
715 (void) WriteBlob(image,MagickMaxBufferExtent-count,output_buffer);
716 }
717 output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
718 }
719 input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
720 JxlThreadParallelRunnerDestroy(runner);
721 JxlEncoderDestroy(encoder);
722 if (encoder_status != JXL_ENC_SUCCESS)
723 ThrowWriterException(CoderError,"UnableToWriteImageData");
724 (void) CloseBlob(image);
725 return(status);
726 }
727 #endif
728