/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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 % % % % The JPEG XL Project % % September 2019 % % % % % % Copyright 1999-2020 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/static.h" #include "MagickCore/string_.h" #include "MagickCore/module.h" #if defined(MAGICKCORE_JXL_DELEGATE) #include #include /* Forward declarations. */ static MagickBooleanType WriteJXLImage(const ImageInfo *,Image *); #endif #if defined(MAGICKCORE_JXL_DELEGATE) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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. % */ /* Typedef declarations. */ typedef struct OutBuffer { unsigned char *data; size_t size; } OutBuffer; static size_t AllocOutput(void* data, const uint8_t* buf, size_t count) { OutBuffer *buffer=(OutBuffer *) data; buffer->data=ResizeMagickMemory(buffer->data,buffer->size+count); if (!buffer->data) return 0; memcpy(buffer->data+buffer->size, buf, count); buffer->size+=count; return(count); } static Image *ReadJXLImage(const ImageInfo *image_info,ExceptionInfo *exception) { Image *temp_image, *result; MagickBooleanType status; OutBuffer b; unsigned char *jxl; /* TODO: do we need an Image here? No pixels are needed, but OpenBlob needs an Image. */ temp_image=AcquireImage(image_info, exception); status=OpenBlob(image_info,temp_image,ReadBinaryBlobMode,exception); jxl=NULL; size_t jxlsize = 0; if (status == MagickTrue) { jxlsize=(size_t) GetBlobSize(temp_image); jxl=(unsigned char *) AcquireMagickMemory(jxlsize); size_t num_read=ReadBlob(temp_image,jxlsize,jxl); if (num_read != jxlsize) status=MagickFalse; } (void) DestroyImage(temp_image); b.data=NULL; b.size=0; if (status == MagickTrue) { status=DecodeBrunsli(jxlsize,jxl,&b,AllocOutput) == 1 ? MagickTrue : MagickFalse; } (void) RelinquishMagickMemory(jxl); result=NULL; if (status == MagickTrue) { ImageInfo* temp_info=AcquireImageInfo(); SetImageInfoBlob(temp_info,b.data,b.size); result=BlobToImage(temp_info,b.data,b.size,exception); (void) DestroyImageInfo(temp_info); } (void) RelinquishMagickMemory(b.data); return(result); } #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->note=ConstantString( "JPEG1 recompression as specified in https://arxiv.org/pdf/1908.03565.pdf" " page 135. Full JPEG XL support will be implemented in this coder later."); (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; Image *temp_image; ImageInfo *temp_info; MagickBooleanType status; OutBuffer b; size_t jpegsize; unsigned char *jpeg; exception=AcquireExceptionInfo(); /* TODO: can cloning the image be avoided? The pixels don't need to be cloned, only filename or blob information. ImageToBlob overwrites this information. */ temp_image=CloneImage(image, 0, 0, MagickTrue, exception); temp_info=AcquireImageInfo(); (void) CopyMagickString(temp_image->magick,"JPG",MaxTextExtent); jpeg=ImageToBlob(temp_info,temp_image,&jpegsize,exception); (void) DestroyImage(temp_image); (void) DestroyImageInfo(temp_info); b.data=NULL; b.size=0; status=EncodeBrunsli(jpegsize,jpeg,&b,AllocOutput) == 1 ? MagickTrue : MagickFalse; if (status == MagickTrue) { status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); } if (status == MagickTrue) { WriteBlob(image,b.size,b.data); CloseBlob(image); } (void) RelinquishMagickMemory(b.data); if(exception) exception=DestroyExceptionInfo(exception); return(status); } #endif