diff options
-rw-r--r-- | coders/webp.c | 385 | ||||
-rwxr-xr-x | configure | 6 | ||||
-rwxr-xr-x | configure.ac | 1 | ||||
-rwxr-xr-x | packaging/GraphicsMagick.spec | 1 |
4 files changed, 333 insertions, 60 deletions
diff --git a/coders/webp.c b/coders/webp.c index 04016e7..711fcf5 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, MagickFreeMemory(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) { MagickFreeMemory(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; @@ -259,79 +520,91 @@ static Image *ReadWEBPImage(const ImageInfo *image_info, MagickFreeMemory(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) - { - MagickFreeMemory(stream); - ThrowReaderException(CoderError,NoDataReturned,image); - } + if (pixels == (unsigned char *) NULL) + { + MagickFreeMemory(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 */ - { - uint32_t webp_flags=0; - WebPData flag_data; - WebPData content={stream,length}; + /* Read features out of the WebP container */ + { + 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) { - WebPMuxGetChunk(mux,"ICCP",&flag_data); - AppendImageProfile(image,"ICC",flag_data.bytes,flag_data.size); - } + if (webp_flags & ICCP_FLAG) { + WebPMuxGetChunk(mux,"ICCP",&flag_data); + AppendImageProfile(image,"ICC",flag_data.bytes,flag_data.size); + } - if (webp_flags & EXIF_FLAG) { - WebPMuxGetChunk(mux,"EXIF",&flag_data); - AppendImageProfile(image,"EXIF",flag_data.bytes,flag_data.size); - } + if (webp_flags & EXIF_FLAG) { + WebPMuxGetChunk(mux,"EXIF",&flag_data); + AppendImageProfile(image,"EXIF",flag_data.bytes,flag_data.size); + } - if (webp_flags & XMP_FLAG) { - WebPMuxGetChunk(mux,"XMP",&flag_data); - AppendImageProfile(image,"XMP",flag_data.bytes,flag_data.size); + if (webp_flags & XMP_FLAG) { + WebPMuxGetChunk(mux,"XMP",&flag_data); + AppendImageProfile(image,"XMP",flag_data.bytes,flag_data.size); + } + + WebPMuxDelete(mux); } +#endif /* defined(SUPPORT_WEBP_MUX) */ - WebPMuxDelete(mux); + /* + Free scale resource. + */ + free(pixels); + pixels=(unsigned char *) NULL; } -#endif /* defined(SUPPORT_WEBP_MUX) */ - /* - Free scale resource. - */ - free(pixels); - pixels=(unsigned char *) NULL; MagickFreeMemory(stream); CloseBlob(image); return(image); @@ -27510,7 +27510,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. */ @@ -27541,7 +27541,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 @@ -27552,7 +27552,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 6d77a20..0776701 100755 --- a/configure.ac +++ b/configure.ac @@ -2534,6 +2534,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 |