diff options
author | jiyong.min <jiyong.min@samsung.com> | 2020-07-10 13:31:36 +0900 |
---|---|---|
committer | backto.kim <backto.kim@samsung.com> | 2021-12-07 10:34:28 +0900 |
commit | fde3d2c67bd7f9c4d2e993cd619febe13c8895ab (patch) | |
tree | 04715e81944fbbcb37cfb32b3e1f4215bfecf846 | |
parent | c0c0ebec79fe3e52313539f42c9d93bfae86b401 (diff) | |
download | GraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.tar.gz GraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.tar.bz2 GraphicsMagick-fde3d2c67bd7f9c4d2e993cd619febe13c8895ab.zip |
Apply animated WebP patch for WebP coder
- WebP is based on RIFF container and it provides additional container
for animation. If a WebP image contains animation container,
image data is included in the animation container.
But GraphicsMagick does not support animation container.
For the reason, GraphicsMagick return invalid image for animated WebP.
(The mandatory image data is not found)
So we add to parse animation container and decode 1 frame.
- Tizen platform supports 1 frame for animated image format.
Change-Id: I1894517769d1b097f7103fe55a525211e7519589
-rw-r--r-- | coders/webp.c | 378 | ||||
-rwxr-xr-x | configure | 6 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rwxr-xr-x | packaging/GraphicsMagick.spec | 1 |
4 files changed, 328 insertions, 58 deletions
diff --git a/coders/webp.c b/coders/webp.c index 54eb608..9529b4e 100644 --- a/coders/webp.c +++ b/coders/webp.c @@ -61,6 +61,7 @@ static unsigned int WriteWEBPImage(const ImageInfo *,Image *); #if defined(HasWEBP) #include <webp/decode.h> #include <webp/encode.h> +#include <webp/demux.h> /* Release versions vs ABI versions (found in src/webp/encode.h) @@ -138,6 +139,252 @@ static MagickTsdKey_t tsd_key = (MagickTsdKey_t) 0; % o exception: return any errors or warnings in this structure. % */ + +/* + Apply ImageMagick patch for animated WebP. + Decoding only the first frame of the animation image is supported. +*/ +#ifndef __TIZEN__ +#define __TIZEN__ +#endif + +#if defined(__TIZEN__) +static inline uint32_t ReadWebPLSBWord( + const unsigned char *restrict data) +{ + register const unsigned char + *p; + + register uint32_t + value; + + p=data; + value=(uint32_t) (*p++); + value|=((uint32_t) (*p++)) << 8; + value|=((uint32_t) (*p++)) << 16; + value|=((uint32_t) (*p++)) << 24; + return(value); +} + +static int FillBasicWEBPInfo(Image *image,const uint8_t *stream,size_t length, + WebPDecoderConfig *configure) +{ + WebPBitstreamFeatures + *restrict features = &configure->input; + + int + webp_status; + + webp_status=WebPGetFeatures(stream,length,features); + + if (webp_status != VP8_STATUS_OK) + return(webp_status); + + image->columns=(size_t) features->width; + image->rows=(size_t) features->height; + image->depth=8; + image->matte=(features->has_alpha ? MagickTrue : MagickFalse); + + return(webp_status); +} + +static int ReadSingleWEBPImage(Image *image,const uint8_t *stream, + size_t length,WebPDecoderConfig *configure,ExceptionInfo *exception, + MagickBool is_first) +{ + int + webp_status; + + register unsigned char + *p; + + register PixelPacket + *q; + + register size_t + x; + + size_t + canvas_width, + canvas_height, + image_width, + image_height; + + ssize_t + x_offset, + y_offset, + y; + + WebPDecBuffer + *restrict webp_image = &configure->output; + + if (is_first) + { + canvas_width=image->columns; + canvas_height=image->rows; + x_offset=image->page.x; + y_offset=image->page.y; + image->page.x=0; + image->page.y=0; + } + else + { + x_offset=0; + y_offset=0; + } + webp_status=FillBasicWEBPInfo(image,stream,length,configure); + image_width=image->columns; + image_height=image->rows; + if (is_first) + { + image->columns=canvas_width; + image->rows=canvas_height; + } + + if (webp_status != VP8_STATUS_OK) + return(webp_status); + + webp_status=WebPDecode(stream,length,configure); + (void) LogMagickEvent(CoderEvent,GetMagickModule(),"WebPDecode webp_status=%d",webp_status); + if (webp_status != VP8_STATUS_OK) + return(webp_status); + + p=(unsigned char *) webp_image->u.RGBA.rgba; + + for (y=0; y < (size_t) image->rows; y++) + { + q=GetImagePixelsEx(image,0,y,image->columns,1,exception); + if (q == (PixelPacket *) NULL) + break; + + for (x=0; x < (size_t) image->columns; x++) + { + SetRedSample(q,ScaleCharToQuantum(*p++)); + SetGreenSample(q,ScaleCharToQuantum(*p++)); + SetBlueSample(q,ScaleCharToQuantum(*p++)); + if (image->matte) + SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++)); + else + SetOpacitySample(q,OpaqueOpacity); + q++; + } + + if (!SyncImagePixels(image)) + break; + } + + WebPFreeDecBuffer(webp_image); +#if defined(MAGICKCORE_WEBPMUX_DELEGATE) + { + uint32_t + webp_flags = 0; + + WebPData + chunk, + content; + + WebPMux + *mux; + + /* + Extract any profiles. + */ + content.bytes=stream; + content.size=length; + mux=WebPMuxCreate(&content,0); + (void) memset(&chunk,0,sizeof(chunk)); + WebPMuxGetFeatures(mux,&webp_flags); + if (webp_flags & ICCP_FLAG) + { + WebPMuxGetChunk(mux,"ICCP",&chunk); + AppendImageProfile(image,"ICC",chunk.bytes,chunk.size); + } + if (webp_flags & EXIF_FLAG) + { + WebPMuxGetChunk(mux,"EXIF",&chunk); + AppendImageProfile(image,"EXIF",chunk.bytes,chunk.size); + } + if (webp_flags & XMP_FLAG) + { + WebPMuxGetChunk(mux,"XMP",&chunk); + AppendImageProfile(image,"XMP",chunk.bytes,chunk.size); + } + WebPMuxDelete(mux); + } +#endif + return(webp_status); +} + +static int ReadAnimatedWEBPImage(const ImageInfo *image_info,Image *image, + uint8_t *stream,size_t length,WebPDecoderConfig *configure, + ExceptionInfo *exception) +{ + Image + *original_image; + + int + image_count, + webp_status; + + size_t + canvas_width, + canvas_height; + + WebPData + data; + + WebPDemuxer + *demux; + + WebPIterator + iter; + + image_count=0; + webp_status=0; + original_image=image; + webp_status=FillBasicWEBPInfo(image,stream,length,configure); + canvas_width=image->columns; + canvas_height=image->rows; + data.bytes=stream; + data.size=length; + demux=WebPDemux(&data); + if (WebPDemuxGetFrame(demux,1,&iter)) { + do { + if (image_count != 0) + { + if (GetNextImageInList(image) == (Image *) NULL) + break; + image=SyncNextImageInList(image); + CloneImageAttributes(image,original_image); + image->page.x=iter.x_offset; + image->page.y=iter.y_offset; + webp_status=ReadSingleWEBPImage(image,iter.fragment.bytes, + iter.fragment.size,configure,exception,MagickFalse); + } + else + { + image->page.x=iter.x_offset; + image->page.y=iter.y_offset; + webp_status=ReadSingleWEBPImage(image,iter.fragment.bytes, + iter.fragment.size,configure,exception,MagickTrue); + } + if (webp_status != VP8_STATUS_OK) + break; + + image->page.width=canvas_width; + image->page.height=canvas_height; + image->delay=iter.duration/10; + if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) + image->dispose=BackgroundDispose; + image_count++; + } while (WebPDemuxNextFrame(&iter)); + WebPDemuxReleaseIterator(&iter); + } + WebPDemuxDelete(demux); + return(webp_status); +} +#endif /* defined(__TIZEN__) */ + static Image *ReadWEBPImage(const ImageInfo *image_info, ExceptionInfo *exception) { @@ -164,6 +411,11 @@ static Image *ReadWEBPImage(const ImageInfo *image_info, *stream, *pixels; +#if defined(__TIZEN__) + WebPDecoderConfig + configure; +#endif /* defined(__TIZEN__) */ + WebPBitstreamFeatures stream_features; @@ -195,7 +447,15 @@ static Image *ReadWEBPImage(const ImageInfo *image_info, MagickFreeResourceLimitedMemory(stream); ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image); } - if ((webp_status=WebPGetFeatures(stream,length,&stream_features)) != VP8_STATUS_OK) + +#if defined(__TIZEN__) + memset(&configure, 0x00, sizeof(WebPDecoderConfig)); + webp_status=FillBasicWEBPInfo(image,stream,length,&configure); + memcpy(&stream_features,&configure.input,sizeof(WebPBitstreamFeatures)); +#else + webp_status=WebPGetFeatures(stream,length,&stream_features); +#endif /* defined(__TIZEN__) */ + if (webp_status != VP8_STATUS_OK) { MagickFreeResourceLimitedMemory(stream); @@ -244,6 +504,7 @@ static Image *ReadWEBPImage(const ImageInfo *image_info, */ ThrowReaderException(CorruptImageError,CorruptImage,image); } + image->depth=8; image->columns=(size_t) stream_features.width; image->rows=(size_t) stream_features.height; @@ -260,85 +521,94 @@ static Image *ReadWEBPImage(const ImageInfo *image_info, MagickFreeResourceLimitedMemory(stream); ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); } - if (image->matte) - pixels=(unsigned char *) WebPDecodeRGBA(stream,length, + +#if defined(__TIZEN__) + if (configure.input.has_animation) { + webp_status=ReadAnimatedWEBPImage(image_info,image,stream,length, + &configure,exception); + (void) LogMagickEvent(CoderEvent,GetMagickModule(), + "ReadAnimatedWEBPImage return %d",webp_status); + } else +#endif /* defined(__TIZEN__) */ + { + if (image->matte) + pixels=(unsigned char *) WebPDecodeRGBA(stream,length, &stream_features.width, &stream_features.height); - else - pixels=(unsigned char *) WebPDecodeRGB(stream,length, + else + pixels=(unsigned char *) WebPDecodeRGB(stream,length, &stream_features.width, &stream_features.height); - if (pixels == (unsigned char *) NULL) - { - MagickFreeResourceLimitedMemory(stream); - ThrowReaderException(CoderError,NoDataReturned,image); - } + if (pixels == (unsigned char *) NULL) + { + MagickFreeResourceLimitedMemory(stream); + ThrowReaderException(CoderError,NoDataReturned,image); + } - p=pixels; + p=pixels; - for (y=0; y < (size_t) image->rows; y++) - { - q=GetImagePixelsEx(image,0,y,image->columns,1,exception); - if (q == (PixelPacket *) NULL) - break; + for (y=0; y < (size_t) image->rows; y++) + { + q=GetImagePixelsEx(image,0,y,image->columns,1,exception); + if (q == (PixelPacket *) NULL) + break; - for (x=0; x < (size_t) image->columns; x++) - { - SetRedSample(q,ScaleCharToQuantum(*p++)); - SetGreenSample(q,ScaleCharToQuantum(*p++)); - SetBlueSample(q,ScaleCharToQuantum(*p++)); - if (image->matte) - SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++)); - else - SetOpacitySample(q,OpaqueOpacity); - q++; - } + for (x=0; x < (size_t) image->columns; x++) + { + SetRedSample(q,ScaleCharToQuantum(*p++)); + SetGreenSample(q,ScaleCharToQuantum(*p++)); + SetBlueSample(q,ScaleCharToQuantum(*p++)); + if (image->matte) + SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*p++)); + else + SetOpacitySample(q,OpaqueOpacity); + q++; + } - if (!SyncImagePixels(image)) - break; - } + if (!SyncImagePixels(image)) + break; + } #if defined(SUPPORT_WEBP_MUX) - /* - Read features out of the WebP container - https://developers.google.com/speed/webp/docs/container-api - */ - { - uint32_t webp_flags=0; - WebPData flag_data; - WebPData content={stream,length}; + /* + Read features out of the WebP container + https://developers.google.com/speed/webp/docs/container-api + */ + { + uint32_t webp_flags=0; + WebPData flag_data; + WebPData content={stream,length}; - WebPMux *mux=WebPMuxCreate(&content,0); - (void) memset(&flag_data,0,sizeof(flag_data)); - WebPMuxGetFeatures(mux,&webp_flags); + WebPMux *mux=WebPMuxCreate(&content,0); + (void) memset(&flag_data,0,sizeof(flag_data)); + WebPMuxGetFeatures(mux,&webp_flags); - if (webp_flags & ICCP_FLAG) - { + if (webp_flags & ICCP_FLAG) { WebPMuxGetChunk(mux,"ICCP",&flag_data); SetImageProfile(image,"ICC",flag_data.bytes,flag_data.size); } - if (webp_flags & EXIF_FLAG) - { + if (webp_flags & EXIF_FLAG) { WebPMuxGetChunk(mux,"EXIF",&flag_data); SetImageProfile(image,"EXIF",flag_data.bytes,flag_data.size); } - if (webp_flags & XMP_FLAG) - { + if (webp_flags & XMP_FLAG) { WebPMuxGetChunk(mux,"XMP",&flag_data); SetImageProfile(image,"XMP",flag_data.bytes,flag_data.size); } - WebPMuxDelete(mux); - } + WebPMuxDelete(mux); + } #endif /* defined(SUPPORT_WEBP_MUX) */ - /* - Free scale resource. - */ - free(pixels); - pixels=(unsigned char *) NULL; + /* + Free scale resource. + */ + free(pixels); + pixels=(unsigned char *) NULL; + } + MagickFreeResourceLimitedMemory(stream); CloseBlob(image); StopTimer(&image->timer); @@ -27061,7 +27061,7 @@ if ${ac_cv_lib_webp_WebPDecodeRGB+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lwebp $LIBS" +LIBS="-lwebp -lwebpdemux $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -27092,7 +27092,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_webp_WebPDecodeRGB" >&5 $as_echo "$ac_cv_lib_webp_WebPDecodeRGB" >&6; } if test "x$ac_cv_lib_webp_WebPDecodeRGB" = xyes; then : - passed=`expr $passed + 1`; LIB_WEBP='-lwebp' + passed=`expr $passed + 1`; LIB_WEBP='-lwebp -lwebpdemux' else failed=`expr $failed + 1` fi @@ -27103,7 +27103,7 @@ if ${ac_cv_lib_webpmux_WebPMuxSetImage+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS -LIBS="-lwebpmux -lwebp $LIBS" +LIBS="-lwebpmux -lwebp -lwebpdemux $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ diff --git a/configure.ac b/configure.ac index d6a30f6..aa20ab9 100644 --- a/configure.ac +++ b/configure.ac @@ -2511,6 +2511,7 @@ then AC_CHECK_HEADER([webp/encode.h],[passed=`expr $passed + 1`],[failed=`expr $failed + 1`],[]) AC_CHECK_LIB([webp],[WebPDecodeRGB],[passed=`expr $passed + 1`; LIB_WEBP='-lwebp'],[failed=`expr $failed + 1`],[]) AC_CHECK_LIB([webpmux],[WebPMuxSetImage],[LIB_WEBP="$LIB_WEBP -lwebpmux"],[],[-lwebp]) + AC_CHECK_LIB([webpdemux],[WebPDeMuxGetFrame],[LIB_WEBP="$LIB_WEBP -lwebpdemux"],[],[-lwebp]) AC_MSG_CHECKING([if WEBP package is complete]) if test $passed -gt 0 then diff --git a/packaging/GraphicsMagick.spec b/packaging/GraphicsMagick.spec index 519da7c..402f1f9 100755 --- a/packaging/GraphicsMagick.spec +++ b/packaging/GraphicsMagick.spec @@ -14,7 +14,6 @@ BuildRequires: libpng-devel BuildRequires: libtool-ltdl-devel BuildRequires: libxml2-devel BuildRequires: pkgconfig(libwebp) -BuildRequires: pkgconfig(libwebpdecoder) BuildRequires: pkgconfig(libwebpdemux) BuildRequires: pkgconfig(libwebpmux) BuildRequires: xz-devel |