|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% JJJ X X L %
|
|
|
% J X X L %
|
|
|
% J X L %
|
|
|
% J J X X L %
|
|
|
% JJ X X LLLLL %
|
|
|
% %
|
|
|
% %
|
|
|
% Read/Write JPEG XL Lossless JPEG1 Recompression %
|
|
|
% %
|
|
|
% Dirk Lemstra %
|
|
|
% December 2020 %
|
|
|
% %
|
|
|
% %
|
|
|
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
|
|
|
% dedicated to making software imaging solutions freely available. %
|
|
|
% %
|
|
|
% You may not use this file except in compliance with the License. You may %
|
|
|
% obtain a copy of the License at %
|
|
|
% %
|
|
|
% https://imagemagick.org/script/license.php %
|
|
|
% %
|
|
|
% Unless required by applicable law or agreed to in writing, software %
|
|
|
% distributed under the License is distributed on an "AS IS" BASIS, %
|
|
|
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
|
|
|
% See the License for the specific language governing permissions and %
|
|
|
% limitations under the License. %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
%
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
Include declarations.
|
|
|
*/
|
|
|
#include "MagickCore/studio.h"
|
|
|
#include "MagickCore/attribute.h"
|
|
|
#include "MagickCore/blob.h"
|
|
|
#include "MagickCore/blob-private.h"
|
|
|
#include "MagickCore/cache.h"
|
|
|
#include "MagickCore/exception.h"
|
|
|
#include "MagickCore/exception-private.h"
|
|
|
#include "MagickCore/image.h"
|
|
|
#include "MagickCore/image-private.h"
|
|
|
#include "MagickCore/list.h"
|
|
|
#include "MagickCore/magick.h"
|
|
|
#include "MagickCore/memory_.h"
|
|
|
#include "MagickCore/monitor.h"
|
|
|
#include "MagickCore/monitor-private.h"
|
|
|
#include "MagickCore/resource_.h"
|
|
|
#include "MagickCore/static.h"
|
|
|
#include "MagickCore/string_.h"
|
|
|
#include "MagickCore/module.h"
|
|
|
#if defined(MAGICKCORE_JXL_DELEGATE)
|
|
|
#include <jxl/decode.h>
|
|
|
#include <jxl/encode.h>
|
|
|
#include <jxl/thread_parallel_runner.h>
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
Typedef declarations.
|
|
|
*/
|
|
|
typedef struct MemoryManagerInfo
|
|
|
{
|
|
|
Image
|
|
|
*image;
|
|
|
|
|
|
ExceptionInfo
|
|
|
*exception;
|
|
|
} MemoryManagerInfo;
|
|
|
|
|
|
/*
|
|
|
Forward declarations.
|
|
|
*/
|
|
|
static MagickBooleanType
|
|
|
WriteJXLImage(const ImageInfo *,Image *,ExceptionInfo *);
|
|
|
|
|
|
#if defined(MAGICKCORE_JXL_DELEGATE)
|
|
|
static void *JXLAcquireMemory(void *opaque, size_t size)
|
|
|
{
|
|
|
unsigned char
|
|
|
*data;
|
|
|
|
|
|
data=(unsigned char *) AcquireQuantumMemory(size,sizeof(*data));
|
|
|
if (data == (unsigned char *) NULL)
|
|
|
{
|
|
|
MemoryManagerInfo
|
|
|
*memory_manager_info;
|
|
|
|
|
|
memory_manager_info=(MemoryManagerInfo *) opaque;
|
|
|
(void) ThrowMagickException(memory_manager_info->exception,
|
|
|
GetMagickModule(),CoderError,"MemoryAllocationFailed","`%s'",
|
|
|
memory_manager_info->image->filename);
|
|
|
}
|
|
|
return(data);
|
|
|
}
|
|
|
|
|
|
static void JXLRelinquishMemory(void *magick_unused(opaque),void *address)
|
|
|
{
|
|
|
magick_unreferenced(opaque);
|
|
|
(void) RelinquishMagickMemory(address);
|
|
|
}
|
|
|
|
|
|
static inline void JXLSetMemoryManager(JxlMemoryManager *memory_manager,
|
|
|
MemoryManagerInfo *memory_manager_info,Image *image,ExceptionInfo *exception)
|
|
|
{
|
|
|
memory_manager_info->image=image;
|
|
|
memory_manager_info->exception=exception;
|
|
|
memory_manager->opaque=memory_manager_info;
|
|
|
memory_manager->alloc=JXLAcquireMemory;
|
|
|
memory_manager->free=JXLRelinquishMemory;
|
|
|
}
|
|
|
|
|
|
static inline void JXLSetFormat(Image *image,JxlPixelFormat *format)
|
|
|
{
|
|
|
format->num_channels=(image->alpha_trait == BlendPixelTrait) ? 4 : 3;
|
|
|
format->data_type=(image->depth > 8) ? JXL_TYPE_FLOAT : JXL_TYPE_UINT8;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% R e a d J X L I m a g e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% ReadJXLImage() reads a JXL image file and returns it. It allocates
|
|
|
% the memory necessary for the new Image structure and returns a pointer to
|
|
|
% the new image.
|
|
|
%
|
|
|
% The format of the ReadJXLImage method is:
|
|
|
%
|
|
|
% Image *ReadJXLImage(const ImageInfo *image_info,
|
|
|
% ExceptionInfo *exception)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image_info: the image info.
|
|
|
%
|
|
|
% o exception: return any errors or warnings in this structure.
|
|
|
%
|
|
|
*/
|
|
|
static inline OrientationType JXLOrientationToOrientation(
|
|
|
JxlOrientation orientation)
|
|
|
{
|
|
|
switch (orientation)
|
|
|
{
|
|
|
default:
|
|
|
case JXL_ORIENT_IDENTITY:
|
|
|
return TopLeftOrientation;
|
|
|
case JXL_ORIENT_FLIP_HORIZONTAL:
|
|
|
return TopRightOrientation;
|
|
|
case JXL_ORIENT_ROTATE_180:
|
|
|
return BottomRightOrientation;
|
|
|
case JXL_ORIENT_FLIP_VERTICAL:
|
|
|
return BottomLeftOrientation;
|
|
|
case JXL_ORIENT_TRANSPOSE:
|
|
|
return LeftTopOrientation;
|
|
|
case JXL_ORIENT_ROTATE_90_CW:
|
|
|
return RightTopOrientation;
|
|
|
case JXL_ORIENT_ANTI_TRANSPOSE:
|
|
|
return RightBottomOrientation;
|
|
|
case JXL_ORIENT_ROTATE_90_CCW:
|
|
|
return LeftBottomOrientation;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static Image *ReadJXLImage(const ImageInfo *image_info,ExceptionInfo *exception)
|
|
|
{
|
|
|
Image
|
|
|
*image;
|
|
|
|
|
|
JxlPixelFormat
|
|
|
format;
|
|
|
|
|
|
JxlDecoderStatus
|
|
|
events_wanted;
|
|
|
|
|
|
JxlDecoder
|
|
|
*decoder;
|
|
|
|
|
|
JxlDecoderStatus
|
|
|
decoder_status;
|
|
|
|
|
|
JxlMemoryManager
|
|
|
memory_manager;
|
|
|
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
MemoryManagerInfo
|
|
|
memory_manager_info;
|
|
|
|
|
|
size_t
|
|
|
input_size;
|
|
|
|
|
|
unsigned char
|
|
|
*input_buffer,
|
|
|
*output_buffer;
|
|
|
|
|
|
void
|
|
|
*runner;
|
|
|
|
|
|
/*
|
|
|
Open image file.
|
|
|
*/
|
|
|
assert(image_info != (const ImageInfo *) NULL);
|
|
|
assert(image_info->signature == MagickCoreSignature);
|
|
|
if (image_info->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
|
|
|
image_info->filename);
|
|
|
assert(exception != (ExceptionInfo *) NULL);
|
|
|
assert(exception->signature == MagickCoreSignature);
|
|
|
image=AcquireImage(image_info, exception);
|
|
|
status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
|
|
|
if (status == MagickFalse)
|
|
|
{
|
|
|
image=DestroyImageList(image);
|
|
|
return((Image *) NULL);
|
|
|
}
|
|
|
JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
|
|
|
decoder=JxlDecoderCreate(&memory_manager);
|
|
|
if (decoder == (JxlDecoder *) NULL)
|
|
|
ThrowReaderException(CoderError,"MemoryAllocationFailed");
|
|
|
runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
|
|
|
ThreadResource));
|
|
|
if (runner == (void *) NULL)
|
|
|
{
|
|
|
JxlDecoderDestroy(decoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
decoder_status=JxlDecoderSetParallelRunner(decoder,JxlThreadParallelRunner,
|
|
|
runner);
|
|
|
if (decoder_status != JXL_DEC_SUCCESS)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlDecoderDestroy(decoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
events_wanted=JXL_DEC_BASIC_INFO;
|
|
|
if (image_info->ping == MagickFalse)
|
|
|
events_wanted|=JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING;
|
|
|
if (JxlDecoderSubscribeEvents(decoder,events_wanted) != JXL_DEC_SUCCESS)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlDecoderDestroy(decoder);
|
|
|
ThrowReaderException(CoderError,"UnableToReadImageData");
|
|
|
}
|
|
|
input_size=MagickMaxBufferExtent;
|
|
|
input_buffer=AcquireQuantumMemory(input_size,sizeof(*input_buffer));
|
|
|
if (input_buffer == (unsigned char *) NULL)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlDecoderDestroy(decoder);
|
|
|
ThrowReaderException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
output_buffer=(unsigned char *) NULL;
|
|
|
memset(&format,0,sizeof(format));
|
|
|
decoder_status=JXL_DEC_NEED_MORE_INPUT;
|
|
|
while ((decoder_status != JXL_DEC_SUCCESS) &&
|
|
|
(decoder_status != JXL_DEC_ERROR))
|
|
|
{
|
|
|
decoder_status=JxlDecoderProcessInput(decoder);
|
|
|
switch (decoder_status)
|
|
|
{
|
|
|
case JXL_DEC_NEED_MORE_INPUT:
|
|
|
{
|
|
|
size_t
|
|
|
remaining;
|
|
|
|
|
|
ssize_t
|
|
|
count;
|
|
|
|
|
|
remaining=JxlDecoderReleaseInput(decoder);
|
|
|
if (remaining > 0)
|
|
|
memmove(input_buffer,input_buffer+input_size-remaining,remaining);
|
|
|
count=ReadBlob(image,input_size-remaining,input_buffer+remaining);
|
|
|
if (count <= 0)
|
|
|
{
|
|
|
decoder_status=JXL_DEC_SUCCESS;
|
|
|
ThrowMagickException(exception,GetMagickModule(),CoderError,
|
|
|
"InsufficientImageDataInFile","`%s'",image->filename);
|
|
|
break;
|
|
|
}
|
|
|
decoder_status=JxlDecoderSetInput(decoder,(const uint8_t *) input_buffer,
|
|
|
(size_t) count);
|
|
|
if (decoder_status == JXL_DEC_SUCCESS)
|
|
|
decoder_status=JXL_DEC_NEED_MORE_INPUT;
|
|
|
break;
|
|
|
}
|
|
|
case JXL_DEC_BASIC_INFO:
|
|
|
{
|
|
|
JxlBasicInfo
|
|
|
basic_info;
|
|
|
|
|
|
decoder_status=JxlDecoderGetBasicInfo(decoder,&basic_info);
|
|
|
if (decoder_status != JXL_DEC_SUCCESS)
|
|
|
break;
|
|
|
/* For now we dont support images with an animation */
|
|
|
if (basic_info.have_animation == 1)
|
|
|
{
|
|
|
ThrowMagickException(exception,GetMagickModule(),
|
|
|
MissingDelegateError,"NoDecodeDelegateForThisImageFormat",
|
|
|
"`%s'",image->filename);
|
|
|
break;
|
|
|
}
|
|
|
image->columns=basic_info.xsize;
|
|
|
image->rows=basic_info.ysize;
|
|
|
image->depth=basic_info.bits_per_sample;
|
|
|
if (basic_info.alpha_bits != 0)
|
|
|
image->alpha_trait=BlendPixelTrait;
|
|
|
image->orientation=JXLOrientationToOrientation(basic_info.orientation);
|
|
|
decoder_status=JXL_DEC_BASIC_INFO;
|
|
|
break;
|
|
|
}
|
|
|
case JXL_DEC_COLOR_ENCODING:
|
|
|
{
|
|
|
size_t
|
|
|
profile_size;
|
|
|
|
|
|
StringInfo
|
|
|
*profile;
|
|
|
|
|
|
decoder_status=JxlDecoderGetICCProfileSize(decoder,&format,
|
|
|
JXL_COLOR_PROFILE_TARGET_ORIGINAL,&profile_size);
|
|
|
if (decoder_status != JXL_DEC_SUCCESS)
|
|
|
break;
|
|
|
profile=AcquireStringInfo(profile_size);
|
|
|
decoder_status=JxlDecoderGetColorAsICCProfile(decoder,&format,
|
|
|
JXL_COLOR_PROFILE_TARGET_ORIGINAL,GetStringInfoDatum(profile),
|
|
|
profile_size);
|
|
|
if (decoder_status == JXL_DEC_SUCCESS)
|
|
|
decoder_status=JXL_DEC_COLOR_ENCODING;
|
|
|
break;
|
|
|
}
|
|
|
case JXL_DEC_NEED_IMAGE_OUT_BUFFER:
|
|
|
{
|
|
|
size_t
|
|
|
output_size;
|
|
|
|
|
|
JXLSetFormat(image,&format);
|
|
|
decoder_status=JxlDecoderImageOutBufferSize(decoder,&format,
|
|
|
&output_size);
|
|
|
if (decoder_status != JXL_DEC_SUCCESS)
|
|
|
break;
|
|
|
status=SetImageExtent(image,image->columns,image->rows,exception);
|
|
|
if (status == MagickFalse)
|
|
|
break;
|
|
|
output_buffer=AcquireQuantumMemory(output_size,sizeof(*output_buffer));
|
|
|
if (output_buffer == (unsigned char *) NULL)
|
|
|
{
|
|
|
ThrowMagickException(exception,GetMagickModule(),CoderError,
|
|
|
"MemoryAllocationFailed","`%s'",image->filename);
|
|
|
break;
|
|
|
}
|
|
|
decoder_status=JxlDecoderSetImageOutBuffer(decoder,&format,
|
|
|
output_buffer,output_size);
|
|
|
if (decoder_status == JXL_DEC_SUCCESS)
|
|
|
decoder_status=JXL_DEC_NEED_IMAGE_OUT_BUFFER;
|
|
|
}
|
|
|
case JXL_DEC_FULL_IMAGE:
|
|
|
{
|
|
|
if (output_buffer == (unsigned char *) NULL)
|
|
|
{
|
|
|
ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
|
|
|
"UnableToReadImageData","`%s'",image->filename);
|
|
|
break;
|
|
|
}
|
|
|
status=ImportImagePixels(image,0,0,image->columns,image->rows,
|
|
|
image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",
|
|
|
format.data_type == JXL_TYPE_FLOAT ? FloatPixel : CharPixel,
|
|
|
output_buffer,exception);
|
|
|
if (status == MagickFalse)
|
|
|
decoder_status=JXL_DEC_ERROR;
|
|
|
break;
|
|
|
}
|
|
|
case JXL_DEC_SUCCESS:
|
|
|
case JXL_DEC_ERROR:
|
|
|
break;
|
|
|
default:
|
|
|
decoder_status=JXL_DEC_ERROR;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
|
|
|
input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlDecoderDestroy(decoder);
|
|
|
if (decoder_status == JXL_DEC_ERROR)
|
|
|
ThrowReaderException(CorruptImageError,"UnableToReadImageData");
|
|
|
return(image);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% R e g i s t e r J X L I m a g e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% RegisterJXLImage() adds properties for the JXL image format to
|
|
|
% the list of supported formats. The properties include the image format
|
|
|
% tag, a method to read and/or write the format, whether the format
|
|
|
% supports the saving of more than one frame to the same file or blob,
|
|
|
% whether the format supports native in-memory I/O, and a brief
|
|
|
% description of the format.
|
|
|
%
|
|
|
% The format of the RegisterJXLImage method is:
|
|
|
%
|
|
|
% size_t RegisterJXLImage(void)
|
|
|
%
|
|
|
*/
|
|
|
ModuleExport size_t RegisterJXLImage(void)
|
|
|
{
|
|
|
MagickInfo
|
|
|
*entry;
|
|
|
|
|
|
entry=AcquireMagickInfo("JXL", "JXL", "JPEG XL Lossless JPEG1 Recompression");
|
|
|
#if defined(MAGICKCORE_JXL_DELEGATE)
|
|
|
entry->decoder=(DecodeImageHandler *) ReadJXLImage;
|
|
|
entry->encoder=(EncodeImageHandler *) WriteJXLImage;
|
|
|
#endif
|
|
|
entry->flags^=CoderAdjoinFlag;
|
|
|
(void) RegisterMagickInfo(entry);
|
|
|
return(MagickImageCoderSignature);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% U n r e g i s t e r J X L I m a g e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% UnregisterJXLImage() removes format registrations made by the
|
|
|
% JXL module from the list of supported formats.
|
|
|
%
|
|
|
% The format of the UnregisterJXLImage method is:
|
|
|
%
|
|
|
% UnregisterJXLImage(void)
|
|
|
%
|
|
|
*/
|
|
|
ModuleExport void UnregisterJXLImage(void)
|
|
|
{
|
|
|
(void) UnregisterMagickInfo("JXL");
|
|
|
}
|
|
|
|
|
|
#if defined(MAGICKCORE_JXL_DELEGATE)
|
|
|
/*
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
% W r i t e J X L I m a g e %
|
|
|
% %
|
|
|
% %
|
|
|
% %
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
%
|
|
|
% WriteJXLImage() writes a JXL image file and returns it. It
|
|
|
% allocates the memory necessary for the new Image structure and returns a
|
|
|
% pointer to the new image.
|
|
|
%
|
|
|
% The format of the WriteJXLImage method is:
|
|
|
%
|
|
|
% MagickBooleanType WriteJXLImage(const ImageInfo *image_info,
|
|
|
% Image *image)
|
|
|
%
|
|
|
% A description of each parameter follows:
|
|
|
%
|
|
|
% o image_info: the image info.
|
|
|
%
|
|
|
% o image: The image.
|
|
|
%
|
|
|
*/
|
|
|
static MagickBooleanType WriteJXLImage(const ImageInfo *image_info,Image *image,
|
|
|
ExceptionInfo *exception)
|
|
|
{
|
|
|
JxlBasicInfo
|
|
|
basic_info;
|
|
|
|
|
|
JxlEncoder
|
|
|
*encoder;
|
|
|
|
|
|
JxlEncoderOptions
|
|
|
*encoder_options;
|
|
|
|
|
|
JxlEncoderStatus
|
|
|
encoder_status;
|
|
|
|
|
|
JxlMemoryManager
|
|
|
memory_manager;
|
|
|
|
|
|
JxlPixelFormat
|
|
|
format;
|
|
|
|
|
|
MagickBooleanType
|
|
|
status;
|
|
|
|
|
|
MemoryManagerInfo
|
|
|
memory_manager_info;
|
|
|
|
|
|
size_t
|
|
|
bytes_per_row;
|
|
|
|
|
|
unsigned char
|
|
|
*input_buffer;
|
|
|
|
|
|
void
|
|
|
*runner;
|
|
|
|
|
|
/*
|
|
|
Open output image file.
|
|
|
*/
|
|
|
assert(image_info != (const ImageInfo *) NULL);
|
|
|
assert(image_info->signature == MagickCoreSignature);
|
|
|
assert(image != (Image *) NULL);
|
|
|
assert(image->signature == MagickCoreSignature);
|
|
|
if (image->debug != MagickFalse)
|
|
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
|
|
assert(exception != (ExceptionInfo *) NULL);
|
|
|
assert(exception->signature == MagickCoreSignature);
|
|
|
status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
|
|
|
if (status == MagickFalse)
|
|
|
return(status);
|
|
|
JXLSetMemoryManager(&memory_manager,&memory_manager_info,image,exception);
|
|
|
encoder=JxlEncoderCreate(&memory_manager);
|
|
|
if (encoder == (JxlEncoder *) NULL)
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
runner=JxlThreadParallelRunnerCreate(NULL,(size_t) GetMagickResourceLimit(
|
|
|
ThreadResource));
|
|
|
if (runner == (void *) NULL)
|
|
|
{
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
encoder_status=JxlEncoderSetParallelRunner(encoder,JxlThreadParallelRunner,
|
|
|
runner);
|
|
|
if (encoder_status != JXL_ENC_SUCCESS)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
return(MagickFalse);
|
|
|
}
|
|
|
memset(&format,0,sizeof(format));
|
|
|
JXLSetFormat(image,&format);
|
|
|
memset(&basic_info,0,sizeof(basic_info));
|
|
|
basic_info.xsize=(uint32_t) image->columns;
|
|
|
basic_info.ysize=(uint32_t) image->rows;
|
|
|
basic_info.bits_per_sample=8;
|
|
|
if (format.data_type == JXL_TYPE_FLOAT)
|
|
|
{
|
|
|
basic_info.bits_per_sample=32;
|
|
|
basic_info.exponent_bits_per_sample=8;
|
|
|
}
|
|
|
if (image->alpha_trait == BlendPixelTrait)
|
|
|
basic_info.alpha_bits=basic_info.bits_per_sample;
|
|
|
encoder_status=JxlEncoderSetBasicInfo(encoder,&basic_info);
|
|
|
if (encoder_status != JXL_ENC_SUCCESS)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"UnableToWriteImageData");
|
|
|
}
|
|
|
encoder_options=JxlEncoderOptionsCreate(encoder,(JxlEncoderOptions *) NULL);
|
|
|
if (encoder_options == (JxlEncoderOptions *) NULL)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
if (image->quality == 100)
|
|
|
JxlEncoderOptionsSetLossless(encoder_options,JXL_TRUE);
|
|
|
bytes_per_row=image->columns*
|
|
|
((image->alpha_trait == BlendPixelTrait) ? 4 : 3)*
|
|
|
((format.data_type == JXL_TYPE_FLOAT) ? sizeof(float) : sizeof(char));
|
|
|
input_buffer=AcquireQuantumMemory(bytes_per_row,image->rows*
|
|
|
sizeof(*input_buffer));
|
|
|
if (input_buffer == (unsigned char *) NULL)
|
|
|
{
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
status=ExportImagePixels(image,0,0,image->columns,image->rows,
|
|
|
image->alpha_trait == BlendPixelTrait ? "RGBA" : "RGB",
|
|
|
format.data_type == JXL_TYPE_FLOAT ? FloatPixel : CharPixel,
|
|
|
input_buffer,exception);
|
|
|
if (status == MagickFalse)
|
|
|
{
|
|
|
input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
encoder_status=JxlEncoderAddImageFrame(encoder_options,&format,input_buffer,
|
|
|
bytes_per_row*image->rows);
|
|
|
if (encoder_status == JXL_ENC_SUCCESS)
|
|
|
{
|
|
|
unsigned char
|
|
|
*output_buffer;
|
|
|
|
|
|
output_buffer=AcquireQuantumMemory(MagickMaxBufferExtent,
|
|
|
sizeof(*output_buffer));
|
|
|
if (output_buffer == (unsigned char *) NULL)
|
|
|
{
|
|
|
input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
ThrowWriterException(CoderError,"MemoryAllocationFailed");
|
|
|
}
|
|
|
encoder_status=JXL_ENC_NEED_MORE_OUTPUT;
|
|
|
while (encoder_status == JXL_ENC_NEED_MORE_OUTPUT)
|
|
|
{
|
|
|
size_t
|
|
|
count;
|
|
|
|
|
|
unsigned char
|
|
|
*p;
|
|
|
|
|
|
count=MagickMaxBufferExtent;
|
|
|
p=output_buffer;
|
|
|
encoder_status=JxlEncoderProcessOutput(encoder,&p,&count);
|
|
|
(void) WriteBlob(image,MagickMaxBufferExtent-count,output_buffer);
|
|
|
}
|
|
|
output_buffer=(unsigned char *) RelinquishMagickMemory(output_buffer);
|
|
|
}
|
|
|
input_buffer=(unsigned char *) RelinquishMagickMemory(input_buffer);
|
|
|
JxlThreadParallelRunnerDestroy(runner);
|
|
|
JxlEncoderDestroy(encoder);
|
|
|
if (encoder_status != JXL_ENC_SUCCESS)
|
|
|
ThrowWriterException(CoderError,"UnableToWriteImageData");
|
|
|
(void) CloseBlob(image);
|
|
|
return(status);
|
|
|
}
|
|
|
#endif
|